diff --git a/.travis.yml b/.travis.yml index 5c4c8cdf..4e09b148 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,9 +2,10 @@ language: python python: - 2.6 - 2.7 - - 3.2 - 3.3 - 3.4 + - 3.5 + - 3.6 install: - python setup.py install script: python test_tablib.py diff --git a/setup.py b/setup.py index f8c559f2..b7099019 100755 --- a/setup.py +++ b/setup.py @@ -41,24 +41,17 @@ 'tablib.packages.xlwt', 'tablib.packages.xlrd', 'tablib.packages.odf', - 'tablib.packages.openpyxl', - 'tablib.packages.openpyxl.shared', - 'tablib.packages.openpyxl.reader', - 'tablib.packages.openpyxl.writer', 'tablib.packages.yaml', 'tablib.packages.dbfpy', 'tablib.packages.xlwt3', 'tablib.packages.xlrd3', 'tablib.packages.odf3', - 'tablib.packages.openpyxl3', - 'tablib.packages.openpyxl3.shared', - 'tablib.packages.openpyxl3.reader', - 'tablib.packages.openpyxl3.writer', 'tablib.packages.yaml3', 'tablib.packages.dbfpy3' ] install = [ + 'openpyxl', 'unicodecsv', ] @@ -83,12 +76,8 @@ 'Natural Language :: English', 'License :: OSI Approved :: MIT License', 'Programming Language :: Python', - 'Programming Language :: Python :: 2.5', 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3.0', - 'Programming Language :: Python :: 3.1', - 'Programming Language :: Python :: 3.2', 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', diff --git a/tablib/compat.py b/tablib/compat.py index 27ecd08b..533d4808 100644 --- a/tablib/compat.py +++ b/tablib/compat.py @@ -26,7 +26,6 @@ import tablib.packages.xlrd3 as xlrd from tablib.packages.xlrd3.biffh import XLRDError from tablib.packages import markup3 as markup - from tablib.packages import openpyxl3 as openpyxl from tablib.packages.odf3 import opendocument, style, text, table import tablib.packages.dbfpy3 as dbfpy @@ -48,7 +47,6 @@ from tablib.packages.xlrd.biffh import XLRDError from tablib.packages import markup from itertools import ifilter - from tablib.packages import openpyxl from tablib.packages.odf import opendocument, style, text, table import unicodecsv as csv diff --git a/tablib/formats/_xlsx.py b/tablib/formats/_xlsx.py index 411e0fcc..20f55dff 100644 --- a/tablib/formats/_xlsx.py +++ b/tablib/formats/_xlsx.py @@ -11,12 +11,12 @@ else: from cStringIO import StringIO as BytesIO -from tablib.compat import openpyxl +import openpyxl import tablib Workbook = openpyxl.workbook.Workbook ExcelWriter = openpyxl.writer.excel.ExcelWriter -get_column_letter = openpyxl.cell.get_column_letter +get_column_letter = openpyxl.utils.get_column_letter from tablib.compat import unicode @@ -51,7 +51,8 @@ def export_book(databook, freeze_panes=True): """Returns XLSX representation of DataBook.""" wb = Workbook() - wb.worksheets = [] + for sheet in wb.worksheets: + wb.remove_sheet(sheet) for i, dset in enumerate(databook._datasets): ws = wb.create_sheet() ws.title = dset.title if dset.title else 'Sheet%s' % (i) @@ -111,42 +112,38 @@ def dset_sheet(dataset, ws, freeze_panes=True): _offset = i _package.insert((sep[0] + _offset), (sep[1],)) + bold = openpyxl.styles.Font(bold=True) + wrap_text = openpyxl.styles.Alignment(wrap_text=True) + for i, row in enumerate(_package): row_number = i + 1 for j, col in enumerate(row): col_idx = get_column_letter(j + 1) + cell = ws.cell('%s%s' % (col_idx, row_number)) # bold headers if (row_number == 1) and dataset.headers: - # ws.cell('%s%s'%(col_idx, row_number)).value = unicode( - # '%s' % col, errors='ignore') - ws.cell('%s%s'%(col_idx, row_number)).value = unicode(col) - style = ws.get_style('%s%s' % (col_idx, row_number)) - style.font.bold = True + # cell.value = unicode('%s' % col, errors='ignore') + cell.value = unicode(col) + cell.font = bold if freeze_panes: - # As already done in #53, but after Merge lost: # Export Freeze only after first Line ws.freeze_panes = 'A2' # bold separators elif len(row) < dataset.width: - ws.cell('%s%s'%(col_idx, row_number)).value = unicode( - '%s' % col, errors='ignore') - style = ws.get_style('%s%s' % (col_idx, row_number)) - style.font.bold = True + cell.value = unicode('%s' % col, errors='ignore') + cell.font = bold # wrap the rest else: try: if '\n' in col: - ws.cell('%s%s'%(col_idx, row_number)).value = unicode( - '%s' % col, errors='ignore') - style = ws.get_style('%s%s' % (col_idx, row_number)) - style.alignment.wrap_text + cell.value = unicode('%s' % col, errors='ignore') + cell.alignment = wrap_text else: - ws.cell('%s%s'%(col_idx, row_number)).value = unicode( - '%s' % col, errors='ignore') + cell.value = unicode('%s' % col, errors='ignore') except TypeError: - ws.cell('%s%s'%(col_idx, row_number)).value = unicode(col) + cell.value = unicode(col) diff --git a/tablib/packages/openpyxl/__init__.py b/tablib/packages/openpyxl/__init__.py deleted file mode 100644 index 81381d78..00000000 --- a/tablib/packages/openpyxl/__init__.py +++ /dev/null @@ -1,53 +0,0 @@ -# file openpyxl/__init__.py - -# Copyright (c) 2010 openpyxl -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -# @license: http://www.opensource.org/licenses/mit-license.php -# @author: Eric Gazoni - -"""Imports for the openpyxl package.""" - -# package imports -from . import cell -from . import namedrange -from . import style -from . import workbook -from . import worksheet -from . import reader -from . import shared -from . import writer - -# constants - -__major__ = 1 # for major interface/format changes -__minor__ = 5 # for minor interface/format changes -__release__ = 2 # for tweaks, bug-fixes, or development - -__version__ = '%d.%d.%d' % (__major__, __minor__, __release__) - -__author__ = 'Eric Gazoni' -__license__ = 'MIT/Expat' -__author_email__ = 'eric.gazoni@gmail.com' -__maintainer_email__ = 'openpyxl-users@googlegroups.com' -__url__ = 'http://bitbucket.org/ericgazoni/openpyxl/wiki/Home' -__downloadUrl__ = "http://bitbucket.org/ericgazoni/openpyxl/downloads" - -__all__ = ('reader', 'shared', 'writer',) diff --git a/tablib/packages/openpyxl/cell.py b/tablib/packages/openpyxl/cell.py deleted file mode 100644 index 757a8347..00000000 --- a/tablib/packages/openpyxl/cell.py +++ /dev/null @@ -1,384 +0,0 @@ -# file openpyxl/cell.py - -# Copyright (c) 2010 openpyxl -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -# @license: http://www.opensource.org/licenses/mit-license.php -# @author: Eric Gazoni - -"""Manage individual cells in a spreadsheet. - -The Cell class is required to know its value and type, display options, -and any other features of an Excel cell. Utilities for referencing -cells using Excel's 'A1' column/row nomenclature are also provided. - -""" - -__docformat__ = "restructuredtext en" - -# Python stdlib imports -import datetime -import re - -# package imports -from .shared.date_time import SharedDate -from .shared.exc import CellCoordinatesException, \ - ColumnStringIndexException, DataTypeException -from .style import NumberFormat - -# constants -COORD_RE = re.compile('^[$]?([A-Z]+)[$]?(\d+)$') - -ABSOLUTE_RE = re.compile('^[$]?([A-Z]+)[$]?(\d+)(:[$]?([A-Z]+)[$]?(\d+))?$') - -def coordinate_from_string(coord_string): - """Convert a coordinate string like 'B12' to a tuple ('B', 12)""" - match = COORD_RE.match(coord_string.upper()) - if not match: - msg = 'Invalid cell coordinates (%s)' % coord_string - raise CellCoordinatesException(msg) - column, row = match.groups() - return (column, int(row)) - - -def absolute_coordinate(coord_string): - """Convert a coordinate to an absolute coordinate string (B12 -> $B$12)""" - parts = ABSOLUTE_RE.match(coord_string).groups() - - if all(parts[-2:]): - return '$%s$%s:$%s$%s' % (parts[0], parts[1], parts[3], parts[4]) - else: - return '$%s$%s' % (parts[0], parts[1]) - - -def column_index_from_string(column, fast = False): - """Convert a column letter into a column number (e.g. B -> 2) - - Excel only supports 1-3 letter column names from A -> ZZZ, so we - restrict our column names to 1-3 characters, each in the range A-Z. - - .. note:: - - Fast mode is faster but does not check that all letters are capitals between A and Z - - """ - column = column.upper() - - clen = len(column) - - if not fast and not all('A' <= char <= 'Z' for char in column): - msg = 'Column string must contain only characters A-Z: got %s' % column - raise ColumnStringIndexException(msg) - - if clen == 1: - return ord(column[0]) - 64 - elif clen == 2: - return ((1 + (ord(column[0]) - 65)) * 26) + (ord(column[1]) - 64) - elif clen == 3: - return ((1 + (ord(column[0]) - 65)) * 676) + ((1 + (ord(column[1]) - 65)) * 26) + (ord(column[2]) - 64) - elif clen > 3: - raise ColumnStringIndexException('Column string index can not be longer than 3 characters') - else: - raise ColumnStringIndexException('Column string index can not be empty') - - -def get_column_letter(col_idx): - """Convert a column number into a column letter (3 -> 'C') - - Right shift the column col_idx by 26 to find column letters in reverse - order. These numbers are 1-based, and can be converted to ASCII - ordinals by adding 64. - - """ - # these indicies corrospond to A -> ZZZ and include all allowed - # columns - if not 1 <= col_idx <= 18278: - msg = 'Column index out of bounds: %s' % col_idx - raise ColumnStringIndexException(msg) - ordinals = [] - temp = col_idx - while temp: - quotient, remainder = divmod(temp, 26) - # check for exact division and borrow if needed - if remainder == 0: - quotient -= 1 - remainder = 26 - ordinals.append(remainder + 64) - temp = quotient - ordinals.reverse() - return ''.join([chr(ordinal) for ordinal in ordinals]) - - -class Cell(object): - """Describes cell associated properties. - - Properties of interest include style, type, value, and address. - - """ - __slots__ = ('column', - 'row', - '_value', - '_data_type', - 'parent', - 'xf_index', - '_hyperlink_rel') - - ERROR_CODES = {'#NULL!': 0, - '#DIV/0!': 1, - '#VALUE!': 2, - '#REF!': 3, - '#NAME?': 4, - '#NUM!': 5, - '#N/A': 6} - - TYPE_STRING = 's' - TYPE_FORMULA = 'f' - TYPE_NUMERIC = 'n' - TYPE_BOOL = 'b' - TYPE_NULL = 's' - TYPE_INLINE = 'inlineStr' - TYPE_ERROR = 'e' - - VALID_TYPES = [TYPE_STRING, TYPE_FORMULA, TYPE_NUMERIC, TYPE_BOOL, - TYPE_NULL, TYPE_INLINE, TYPE_ERROR] - - RE_PATTERNS = { - 'percentage': re.compile('^\-?[0-9]*\.?[0-9]*\s?\%$'), - 'time': re.compile('^(\d|[0-1]\d|2[0-3]):[0-5]\d(:[0-5]\d)?$'), - 'numeric': re.compile('^\-?([0-9]+\\.?[0-9]*|[0-9]*\\.?[0-9]+)((E|e)\-?[0-9]+)?$'), } - - def __init__(self, worksheet, column, row, value = None): - self.column = column.upper() - self.row = row - # _value is the stored value, while value is the displayed value - self._value = None - self._hyperlink_rel = None - self._data_type = self.TYPE_NULL - if value: - self.value = value - self.parent = worksheet - self.xf_index = 0 - - def __repr__(self): - return "" % (self.parent.title, self.get_coordinate()) - - def check_string(self, value): - """Check string coding, length, and line break character""" - # convert to unicode string - value = unicode(value) - # string must never be longer than 32,767 characters - # truncate if necessary - value = value[:32767] - # we require that newline is represented as "\n" in core, - # not as "\r\n" or "\r" - value = value.replace('\r\n', '\n') - return value - - def check_numeric(self, value): - """Cast value to int or float if necessary""" - if not isinstance(value, (int, float)): - try: - value = int(value) - except ValueError: - value = float(value) - return value - - def set_value_explicit(self, value = None, data_type = TYPE_STRING): - """Coerce values according to their explicit type""" - type_coercion_map = { - self.TYPE_INLINE: self.check_string, - self.TYPE_STRING: self.check_string, - self.TYPE_FORMULA: unicode, - self.TYPE_NUMERIC: self.check_numeric, - self.TYPE_BOOL: bool, } - try: - self._value = type_coercion_map[data_type](value) - except KeyError: - if data_type not in self.VALID_TYPES: - msg = 'Invalid data type: %s' % data_type - raise DataTypeException(msg) - self._data_type = data_type - - def data_type_for_value(self, value): - """Given a value, infer the correct data type""" - if value is None: - data_type = self.TYPE_NULL - elif value is True or value is False: - data_type = self.TYPE_BOOL - elif isinstance(value, (int, float)): - data_type = self.TYPE_NUMERIC - elif not value: - data_type = self.TYPE_STRING - elif isinstance(value, (datetime.datetime, datetime.date)): - data_type = self.TYPE_NUMERIC - elif isinstance(value, basestring) and value[0] == '=': - data_type = self.TYPE_FORMULA - elif self.RE_PATTERNS['numeric'].match(value): - data_type = self.TYPE_NUMERIC - elif value.strip() in self.ERROR_CODES: - data_type = self.TYPE_ERROR - else: - data_type = self.TYPE_STRING - return data_type - - def bind_value(self, value): - """Given a value, infer type and display options.""" - self._data_type = self.data_type_for_value(value) - if value is None: - self.set_value_explicit('', self.TYPE_NULL) - return True - elif self._data_type == self.TYPE_STRING: - # percentage detection - percentage_search = self.RE_PATTERNS['percentage'].match(value) - if percentage_search and value.strip() != '%': - value = float(value.replace('%', '')) / 100.0 - self.set_value_explicit(value, self.TYPE_NUMERIC) - self._set_number_format(NumberFormat.FORMAT_PERCENTAGE) - return True - # time detection - time_search = self.RE_PATTERNS['time'].match(value) - if time_search: - sep_count = value.count(':') #pylint: disable-msg=E1103 - if sep_count == 1: - hours, minutes = [int(bit) for bit in value.split(':')] #pylint: disable-msg=E1103 - seconds = 0 - elif sep_count == 2: - hours, minutes, seconds = \ - [int(bit) for bit in value.split(':')] #pylint: disable-msg=E1103 - days = (hours / 24.0) + (minutes / 1440.0) + \ - (seconds / 86400.0) - self.set_value_explicit(days, self.TYPE_NUMERIC) - self._set_number_format(NumberFormat.FORMAT_DATE_TIME3) - return True - if self._data_type == self.TYPE_NUMERIC: - # date detection - # if the value is a date, but not a date time, make it a - # datetime, and set the time part to 0 - if isinstance(value, datetime.date) and not \ - isinstance(value, datetime.datetime): - value = datetime.datetime.combine(value, datetime.time()) - if isinstance(value, datetime.datetime): - value = SharedDate().datetime_to_julian(date = value) - self.set_value_explicit(value, self.TYPE_NUMERIC) - self._set_number_format(NumberFormat.FORMAT_DATE_YYYYMMDD2) - return True - self.set_value_explicit(value, self._data_type) - - def _get_value(self): - """Return the value, formatted as a date if needed""" - value = self._value - if self.is_date(): - value = SharedDate().from_julian(value) - return value - - def _set_value(self, value): - """Set the value and infer type and display options.""" - self.bind_value(value) - - value = property(_get_value, _set_value, - doc = 'Get or set the value held in the cell.\n\n' - ':rtype: depends on the value (string, float, int or ' - ':class:`datetime.datetime`)') - - def _set_hyperlink(self, val): - """Set value and display for hyperlinks in a cell""" - if self._hyperlink_rel is None: - self._hyperlink_rel = self.parent.create_relationship("hyperlink") - self._hyperlink_rel.target = val - self._hyperlink_rel.target_mode = "External" - if self._value is None: - self.value = val - - def _get_hyperlink(self): - """Return the hyperlink target or an empty string""" - return self._hyperlink_rel is not None and \ - self._hyperlink_rel.target or '' - - hyperlink = property(_get_hyperlink, _set_hyperlink, - doc = 'Get or set the hyperlink held in the cell. ' - 'Automatically sets the `value` of the cell with link text, ' - 'but you can modify it afterwards by setting the ' - '`value` property, and the hyperlink will remain.\n\n' - ':rtype: string') - - @property - def hyperlink_rel_id(self): - """Return the id pointed to by the hyperlink, or None""" - return self._hyperlink_rel is not None and \ - self._hyperlink_rel.id or None - - def _set_number_format(self, format_code): - """Set a new formatting code for numeric values""" - self.style.number_format.format_code = format_code - - @property - def has_style(self): - """Check if the parent worksheet has a style for this cell""" - return self.get_coordinate() in self.parent._styles #pylint: disable-msg=W0212 - - @property - def style(self): - """Returns the :class:`openpyxl.style.Style` object for this cell""" - return self.parent.get_style(self.get_coordinate()) - - @property - def data_type(self): - """Return the data type represented by this cell""" - return self._data_type - - def get_coordinate(self): - """Return the coordinate string for this cell (e.g. 'B12') - - :rtype: string - """ - return '%s%s' % (self.column, self.row) - - @property - def address(self): - """Return the coordinate string for this cell (e.g. 'B12') - - :rtype: string - """ - return self.get_coordinate() - - def offset(self, row = 0, column = 0): - """Returns a cell location relative to this cell. - - :param row: number of rows to offset - :type row: int - - :param column: number of columns to offset - :type column: int - - :rtype: :class:`openpyxl.cell.Cell` - """ - offset_column = get_column_letter(column_index_from_string( - column = self.column) + column) - offset_row = self.row + row - return self.parent.cell('%s%s' % (offset_column, offset_row)) - - def is_date(self): - """Returns whether the value is *probably* a date or not - - :rtype: bool - """ - return (self.has_style - and self.style.number_format.is_date_format() - and isinstance(self._value, (int, float))) diff --git a/tablib/packages/openpyxl/chart.py b/tablib/packages/openpyxl/chart.py deleted file mode 100644 index 56cbb4da..00000000 --- a/tablib/packages/openpyxl/chart.py +++ /dev/null @@ -1,340 +0,0 @@ -''' -Copyright (c) 2010 openpyxl - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - -@license: http://www.opensource.org/licenses/mit-license.php -@author: Eric Gazoni -''' - -import math - -from .style import NumberFormat -from .drawing import Drawing, Shape -from .shared.units import pixels_to_EMU, short_color -from .cell import get_column_letter - -class Axis(object): - - POSITION_BOTTOM = 'b' - POSITION_LEFT = 'l' - - ORIENTATION_MIN_MAX = "minMax" - - def __init__(self): - - self.orientation = self.ORIENTATION_MIN_MAX - self.number_format = NumberFormat() - for attr in ('position','tick_label_position','crosses', - 'auto','label_align','label_offset','cross_between'): - setattr(self, attr, None) - self.min = 0 - self.max = None - self.unit = None - - @classmethod - def default_category(cls): - """ default values for category axes """ - - ax = Axis() - ax.id = 60871424 - ax.cross = 60873344 - ax.position = Axis.POSITION_BOTTOM - ax.tick_label_position = 'nextTo' - ax.crosses = "autoZero" - ax.auto = True - ax.label_align = 'ctr' - ax.label_offset = 100 - return ax - - @classmethod - def default_value(cls): - """ default values for value axes """ - - ax = Axis() - ax.id = 60873344 - ax.cross = 60871424 - ax.position = Axis.POSITION_LEFT - ax.major_gridlines = None - ax.tick_label_position = 'nextTo' - ax.crosses = 'autoZero' - ax.auto = False - ax.cross_between = 'between' - return ax - -class Reference(object): - """ a simple wrapper around a serie of reference data """ - - def __init__(self, sheet, pos1, pos2=None): - - self.sheet = sheet - self.pos1 = pos1 - self.pos2 = pos2 - - def get_type(self): - - if isinstance(self.cache[0], basestring): - return 'str' - else: - return 'num' - - def _get_ref(self): - """ format excel reference notation """ - - if self.pos2: - return '%s!$%s$%s:$%s$%s' % (self.sheet.title, - get_column_letter(self.pos1[1]+1), self.pos1[0]+1, - get_column_letter(self.pos2[1]+1), self.pos2[0]+1) - else: - return '%s!$%s$%s' % (self.sheet.title, - get_column_letter(self.pos1[1]+1), self.pos1[0]+1) - - - def _get_cache(self): - """ read data in sheet - to be used at writing time """ - - cache = [] - if self.pos2: - for row in range(self.pos1[0], self.pos2[0]+1): - for col in range(self.pos1[1], self.pos2[1]+1): - cache.append(self.sheet.cell(row=row, column=col).value) - else: - cell = self.sheet.cell(row=self.pos1[0], column=self.pos1[1]) - cache.append(cell.value) - return cache - - -class Serie(object): - """ a serie of data and possibly associated labels """ - - MARKER_NONE = 'none' - - def __init__(self, values, labels=None, legend=None, color=None, xvalues=None): - - self.marker = Serie.MARKER_NONE - self.values = values - self.xvalues = xvalues - self.labels = labels - self.legend = legend - self.error_bar = None - self._color = color - - def _get_color(self): - return self._color - - def _set_color(self, color): - self._color = short_color(color) - - color = property(_get_color, _set_color) - - def get_min_max(self): - - if self.error_bar: - err_cache = self.error_bar.values._get_cache() - vals = [v + err_cache[i] \ - for i,v in enumerate(self.values._get_cache())] - else: - vals = self.values._get_cache() - return min(vals), max(vals) - - def __len__(self): - - return len(self.values.cache) - -class Legend(object): - - def __init__(self): - - self.position = 'r' - self.layout = None - -class ErrorBar(object): - - PLUS = 1 - MINUS = 2 - PLUS_MINUS = 3 - - def __init__(self, _type, values): - - self.type = _type - self.values = values - -class Chart(object): - """ raw chart class """ - - GROUPING_CLUSTERED = 'clustered' - GROUPING_STANDARD = 'standard' - - BAR_CHART = 1 - LINE_CHART = 2 - SCATTER_CHART = 3 - - def __init__(self, _type, grouping): - - self._series = [] - - # public api - self.type = _type - self.grouping = grouping - self.x_axis = Axis.default_category() - self.y_axis = Axis.default_value() - self.legend = Legend() - self.lang = 'fr-FR' - self.title = '' - self.print_margins = dict(b=.75, l=.7, r=.7, t=.75, header=0.3, footer=.3) - - # the containing drawing - self.drawing = Drawing() - - # the offset for the plot part in percentage of the drawing size - self.width = .6 - self.height = .6 - self.margin_top = self._get_max_margin_top() - self.margin_left = 0 - - # the user defined shapes - self._shapes = [] - - def add_serie(self, serie): - - serie.id = len(self._series) - self._series.append(serie) - self._compute_min_max() - if not None in [s.xvalues for s in self._series]: - self._compute_xmin_xmax() - - def add_shape(self, shape): - - shape._chart = self - self._shapes.append(shape) - - def get_x_units(self): - """ calculate one unit for x axis in EMU """ - - return max([len(s.values._get_cache()) for s in self._series]) - - def get_y_units(self): - """ calculate one unit for y axis in EMU """ - - dh = pixels_to_EMU(self.drawing.height) - return (dh * self.height) / self.y_axis.max - - def get_y_chars(self): - """ estimate nb of chars for y axis """ - - _max = max([max(s.values._get_cache()) for s in self._series]) - return len(str(int(_max))) - - def _compute_min_max(self): - """ compute y axis limits and units """ - - maxi = max([max(s.values._get_cache()) for s in self._series]) - - mul = None - if maxi < 1: - s = str(maxi).split('.')[1] - mul = 10 - for x in s: - if x == '0': - mul *= 10 - else: - break - maxi = maxi * mul - - maxi = math.ceil(maxi * 1.1) - sz = len(str(int(maxi))) - 1 - unit = math.ceil(math.ceil(maxi / pow(10, sz)) * pow(10, sz-1)) - maxi = math.ceil(maxi/unit) * unit - - if mul is not None: - maxi = maxi/mul - unit = unit/mul - - if maxi / unit > 9: - # no more that 10 ticks - unit *= 2 - - self.y_axis.max = maxi - self.y_axis.unit = unit - - def _compute_xmin_xmax(self): - """ compute x axis limits and units """ - - maxi = max([max(s.xvalues._get_cache()) for s in self._series]) - - mul = None - if maxi < 1: - s = str(maxi).split('.')[1] - mul = 10 - for x in s: - if x == '0': - mul *= 10 - else: - break - maxi = maxi * mul - - maxi = math.ceil(maxi * 1.1) - sz = len(str(int(maxi))) - 1 - unit = math.ceil(math.ceil(maxi / pow(10, sz)) * pow(10, sz-1)) - maxi = math.ceil(maxi/unit) * unit - - if mul is not None: - maxi = maxi/mul - unit = unit/mul - - if maxi / unit > 9: - # no more that 10 ticks - unit *= 2 - - self.x_axis.max = maxi - self.x_axis.unit = unit - - def _get_max_margin_top(self): - - mb = Shape.FONT_HEIGHT + Shape.MARGIN_BOTTOM - plot_height = self.drawing.height * self.height - return float(self.drawing.height - plot_height - mb)/self.drawing.height - - def _get_min_margin_left(self): - - ml = (self.get_y_chars() * Shape.FONT_WIDTH) + Shape.MARGIN_LEFT - return float(ml)/self.drawing.width - - def _get_margin_top(self): - """ get margin in percent """ - - return min(self.margin_top, self._get_max_margin_top()) - - def _get_margin_left(self): - - return max(self._get_min_margin_left(), self.margin_left) - -class BarChart(Chart): - def __init__(self): - super(BarChart, self).__init__(Chart.BAR_CHART, Chart.GROUPING_CLUSTERED) - -class LineChart(Chart): - def __init__(self): - super(LineChart, self).__init__(Chart.LINE_CHART, Chart.GROUPING_STANDARD) - -class ScatterChart(Chart): - def __init__(self): - super(ScatterChart, self).__init__(Chart.SCATTER_CHART, Chart.GROUPING_STANDARD) - - diff --git a/tablib/packages/openpyxl/drawing.py b/tablib/packages/openpyxl/drawing.py deleted file mode 100644 index 610324ec..00000000 --- a/tablib/packages/openpyxl/drawing.py +++ /dev/null @@ -1,401 +0,0 @@ -''' -Copyright (c) 2010 openpyxl - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - -@license: http://www.opensource.org/licenses/mit-license.php -@author: Eric Gazoni -''' - -import math -from .style import Color -from .shared.units import pixels_to_EMU, EMU_to_pixels, short_color - -class Shadow(object): - - SHADOW_BOTTOM = 'b' - SHADOW_BOTTOM_LEFT = 'bl' - SHADOW_BOTTOM_RIGHT = 'br' - SHADOW_CENTER = 'ctr' - SHADOW_LEFT = 'l' - SHADOW_TOP = 't' - SHADOW_TOP_LEFT = 'tl' - SHADOW_TOP_RIGHT = 'tr' - - def __init__(self): - self.visible = False - self.blurRadius = 6 - self.distance = 2 - self.direction = 0 - self.alignment = self.SHADOW_BOTTOM_RIGHT - self.color = Color(Color.BLACK) - self.alpha = 50 - -class Drawing(object): - """ a drawing object - eg container for shapes or charts - we assume user specifies dimensions in pixels; units are - converted to EMU in the drawing part - """ - - count = 0 - - def __init__(self): - - self.name = '' - self.description = '' - self.coordinates = ((1,2), (16,8)) - self.left = 0 - self.top = 0 - self._width = EMU_to_pixels(200000) - self._height = EMU_to_pixels(1828800) - self.resize_proportional = False - self.rotation = 0 -# self.shadow = Shadow() - - def _set_width(self, w): - - if self.resize_proportional and w: - ratio = self._height / self._width - self._height = round(ratio * w) - self._width = w - - def _get_width(self): - - return self._width - - width = property(_get_width, _set_width) - - def _set_height(self, h): - - if self.resize_proportional and h: - ratio = self._width / self._height - self._width = round(ratio * h) - self._height = h - - def _get_height(self): - - return self._height - - height = property(_get_height, _set_height) - - def set_dimension(self, w=0, h=0): - - xratio = w / self._width - yratio = h / self._height - - if self.resize_proportional and w and h: - if (xratio * self._height) < h: - self._height = math.ceil(xratio * self._height) - self._width = width - else: - self._width = math.ceil(yratio * self._width) - self._height = height - - def get_emu_dimensions(self): - """ return (x, y, w, h) in EMU """ - - return (pixels_to_EMU(self.left), pixels_to_EMU(self.top), - pixels_to_EMU(self._width), pixels_to_EMU(self._height)) - - -class Shape(object): - """ a drawing inside a chart - coordiantes are specified by the user in the axis units - """ - - MARGIN_LEFT = 6 + 13 + 1 - MARGIN_BOTTOM = 17 + 11 - - FONT_WIDTH = 7 - FONT_HEIGHT = 8 - - ROUND_RECT = 'roundRect' - RECT = 'rect' - - # other shapes to define : - ''' - "line" - "lineInv" - "triangle" - "rtTriangle" - "diamond" - "parallelogram" - "trapezoid" - "nonIsoscelesTrapezoid" - "pentagon" - "hexagon" - "heptagon" - "octagon" - "decagon" - "dodecagon" - "star4" - "star5" - "star6" - "star7" - "star8" - "star10" - "star12" - "star16" - "star24" - "star32" - "roundRect" - "round1Rect" - "round2SameRect" - "round2DiagRect" - "snipRoundRect" - "snip1Rect" - "snip2SameRect" - "snip2DiagRect" - "plaque" - "ellipse" - "teardrop" - "homePlate" - "chevron" - "pieWedge" - "pie" - "blockArc" - "donut" - "noSmoking" - "rightArrow" - "leftArrow" - "upArrow" - "downArrow" - "stripedRightArrow" - "notchedRightArrow" - "bentUpArrow" - "leftRightArrow" - "upDownArrow" - "leftUpArrow" - "leftRightUpArrow" - "quadArrow" - "leftArrowCallout" - "rightArrowCallout" - "upArrowCallout" - "downArrowCallout" - "leftRightArrowCallout" - "upDownArrowCallout" - "quadArrowCallout" - "bentArrow" - "uturnArrow" - "circularArrow" - "leftCircularArrow" - "leftRightCircularArrow" - "curvedRightArrow" - "curvedLeftArrow" - "curvedUpArrow" - "curvedDownArrow" - "swooshArrow" - "cube" - "can" - "lightningBolt" - "heart" - "sun" - "moon" - "smileyFace" - "irregularSeal1" - "irregularSeal2" - "foldedCorner" - "bevel" - "frame" - "halfFrame" - "corner" - "diagStripe" - "chord" - "arc" - "leftBracket" - "rightBracket" - "leftBrace" - "rightBrace" - "bracketPair" - "bracePair" - "straightConnector1" - "bentConnector2" - "bentConnector3" - "bentConnector4" - "bentConnector5" - "curvedConnector2" - "curvedConnector3" - "curvedConnector4" - "curvedConnector5" - "callout1" - "callout2" - "callout3" - "accentCallout1" - "accentCallout2" - "accentCallout3" - "borderCallout1" - "borderCallout2" - "borderCallout3" - "accentBorderCallout1" - "accentBorderCallout2" - "accentBorderCallout3" - "wedgeRectCallout" - "wedgeRoundRectCallout" - "wedgeEllipseCallout" - "cloudCallout" - "cloud" - "ribbon" - "ribbon2" - "ellipseRibbon" - "ellipseRibbon2" - "leftRightRibbon" - "verticalScroll" - "horizontalScroll" - "wave" - "doubleWave" - "plus" - "flowChartProcess" - "flowChartDecision" - "flowChartInputOutput" - "flowChartPredefinedProcess" - "flowChartInternalStorage" - "flowChartDocument" - "flowChartMultidocument" - "flowChartTerminator" - "flowChartPreparation" - "flowChartManualInput" - "flowChartManualOperation" - "flowChartConnector" - "flowChartPunchedCard" - "flowChartPunchedTape" - "flowChartSummingJunction" - "flowChartOr" - "flowChartCollate" - "flowChartSort" - "flowChartExtract" - "flowChartMerge" - "flowChartOfflineStorage" - "flowChartOnlineStorage" - "flowChartMagneticTape" - "flowChartMagneticDisk" - "flowChartMagneticDrum" - "flowChartDisplay" - "flowChartDelay" - "flowChartAlternateProcess" - "flowChartOffpageConnector" - "actionButtonBlank" - "actionButtonHome" - "actionButtonHelp" - "actionButtonInformation" - "actionButtonForwardNext" - "actionButtonBackPrevious" - "actionButtonEnd" - "actionButtonBeginning" - "actionButtonReturn" - "actionButtonDocument" - "actionButtonSound" - "actionButtonMovie" - "gear6" - "gear9" - "funnel" - "mathPlus" - "mathMinus" - "mathMultiply" - "mathDivide" - "mathEqual" - "mathNotEqual" - "cornerTabs" - "squareTabs" - "plaqueTabs" - "chartX" - "chartStar" - "chartPlus" - ''' - - def __init__(self, coordinates=((0,0), (1,1)), text=None, scheme="accent1"): - - self.coordinates = coordinates # in axis unit - self.text = text - self.scheme = scheme - self.style = Shape.RECT - self._border_width = 3175 # in EMU - self._border_color = Color.BLACK[2:] #"F3B3C5" - self._color = Color.WHITE[2:] - self._text_color = Color.BLACK[2:] - - def _get_border_color(self): - return self._border_color - - def _set_border_color(self, color): - self._border_color = short_color(color) - - border_color = property(_get_border_color, _set_border_color) - - def _get_color(self): - return self._color - - def _set_color(self, color): - self._color = short_color(color) - - color = property(_get_color, _set_color) - - def _get_text_color(self): - return self._text_color - - def _set_text_color(self, color): - self._text_color = short_color(color) - - text_color = property(_get_text_color, _set_text_color) - - def _get_border_width(self): - - return EMU_to_pixels(self._border_width) - - def _set_border_width(self, w): - - self._border_width = pixels_to_EMU(w) - # print self._border_width - - border_width = property(_get_border_width, _set_border_width) - - def get_coordinates(self): - """ return shape coordinates in percentages (left, top, right, bottom) """ - - (x1, y1), (x2, y2) = self.coordinates - - drawing_width = pixels_to_EMU(self._chart.drawing.width) - drawing_height = pixels_to_EMU(self._chart.drawing.height) - plot_width = drawing_width * self._chart.width - plot_height = drawing_height * self._chart.height - - margin_left = self._chart._get_margin_left() * drawing_width - xunit = plot_width / self._chart.get_x_units() - - margin_top = self._chart._get_margin_top() * drawing_height - yunit = self._chart.get_y_units() - - x_start = (margin_left + (float(x1) * xunit)) / drawing_width - y_start = (margin_top + plot_height - (float(y1) * yunit)) / drawing_height - - x_end = (margin_left + (float(x2) * xunit)) / drawing_width - y_end = (margin_top + plot_height - (float(y2) * yunit)) / drawing_height - - def _norm_pct(pct): - """ force shapes to appear by truncating too large sizes """ - if pct>1: pct = 1 - elif pct<0: pct = 0 - return pct - - # allow user to specify y's in whatever order - # excel expect y_end to be lower - if y_end < y_start: - y_end, y_start = y_start, y_end - - return (_norm_pct(x_start), _norm_pct(y_start), - _norm_pct(x_end), _norm_pct(y_end)) diff --git a/tablib/packages/openpyxl/namedrange.py b/tablib/packages/openpyxl/namedrange.py deleted file mode 100644 index 85b08a88..00000000 --- a/tablib/packages/openpyxl/namedrange.py +++ /dev/null @@ -1,68 +0,0 @@ -# file openpyxl/namedrange.py - -# Copyright (c) 2010 openpyxl -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -# @license: http://www.opensource.org/licenses/mit-license.php -# @author: Eric Gazoni - -"""Track named groups of cells in a worksheet""" - -# Python stdlib imports -import re - -# package imports -from .shared.exc import NamedRangeException - -# constants -NAMED_RANGE_RE = re.compile("'?([^']*)'?!((\$([A-Za-z]+))?\$([0-9]+)(:(\$([A-Za-z]+))?(\$([0-9]+)))?)$") - -class NamedRange(object): - """A named group of cells""" - __slots__ = ('name', 'destinations', 'local_only') - - def __init__(self, name, destinations): - self.name = name - self.destinations = destinations - self.local_only = False - - def __str__(self): - return ','.join(['%s!%s' % (sheet, name) for sheet, name in self.destinations]) - - def __repr__(self): - - return '<%s "%s">' % (self.__class__.__name__, str(self)) - - -def split_named_range(range_string): - """Separate a named range into its component parts""" - - destinations = [] - - for range_string in range_string.split(','): - - match = NAMED_RANGE_RE.match(range_string) - if not match: - raise NamedRangeException('Invalid named range string: "%s"' % range_string) - else: - sheet_name, xlrange = match.groups()[:2] - destinations.append((sheet_name, xlrange)) - - return destinations diff --git a/tablib/packages/openpyxl/reader/__init__.py b/tablib/packages/openpyxl/reader/__init__.py deleted file mode 100644 index 9b0ee2f5..00000000 --- a/tablib/packages/openpyxl/reader/__init__.py +++ /dev/null @@ -1,33 +0,0 @@ -# file openpyxl/reader/__init__.py - -# Copyright (c) 2010 openpyxl -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -# @license: http://www.opensource.org/licenses/mit-license.php -# @author: Eric Gazoni - -"""Imports for the openpyxl.reader namespace.""" - -# package imports -from ..reader import excel -from ..reader import strings -from ..reader import style -from ..reader import workbook -from ..reader import worksheet diff --git a/tablib/packages/openpyxl/reader/excel.py b/tablib/packages/openpyxl/reader/excel.py deleted file mode 100644 index 16c3f91b..00000000 --- a/tablib/packages/openpyxl/reader/excel.py +++ /dev/null @@ -1,109 +0,0 @@ -# file openpyxl/reader/excel.py - -# Copyright (c) 2010 openpyxl -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -# @license: http://www.opensource.org/licenses/mit-license.php -# @author: Eric Gazoni - -"""Read an xlsx file into Python""" - -# Python stdlib imports -from zipfile import ZipFile, ZIP_DEFLATED, BadZipfile - -# package imports -from ..shared.exc import OpenModeError, InvalidFileException -from ..shared.ooxml import ARC_SHARED_STRINGS, ARC_CORE, ARC_APP, \ - ARC_WORKBOOK, PACKAGE_WORKSHEETS, ARC_STYLE -from ..workbook import Workbook -from ..reader.strings import read_string_table -from ..reader.style import read_style_table -from ..reader.workbook import read_sheets_titles, read_named_ranges, \ - read_properties_core, get_sheet_ids -from ..reader.worksheet import read_worksheet -from ..reader.iter_worksheet import unpack_worksheet - -def load_workbook(filename, use_iterators = False): - """Open the given filename and return the workbook - - :param filename: the path to open - :type filename: string - - :param use_iterators: use lazy load for cells - :type use_iterators: bool - - :rtype: :class:`openpyxl.workbook.Workbook` - - .. note:: - - When using lazy load, all worksheets will be :class:`openpyxl.reader.iter_worksheet.IterableWorksheet` - and the returned workbook will be read-only. - - """ - - if isinstance(filename, file): - # fileobject must have been opened with 'rb' flag - # it is required by zipfile - if 'b' not in filename.mode: - raise OpenModeError("File-object must be opened in binary mode") - - try: - archive = ZipFile(filename, 'r', ZIP_DEFLATED) - except (BadZipfile, RuntimeError, IOError, ValueError): - raise InvalidFileException() - wb = Workbook() - - if use_iterators: - wb._set_optimized_read() - - try: - _load_workbook(wb, archive, filename, use_iterators) - except KeyError: - raise InvalidFileException() - finally: - archive.close() - return wb - -def _load_workbook(wb, archive, filename, use_iterators): - - # get workbook-level information - wb.properties = read_properties_core(archive.read(ARC_CORE)) - try: - string_table = read_string_table(archive.read(ARC_SHARED_STRINGS)) - except KeyError: - string_table = {} - style_table = read_style_table(archive.read(ARC_STYLE)) - - # get worksheets - wb.worksheets = [] # remove preset worksheet - sheet_names = read_sheets_titles(archive.read(ARC_APP)) - for i, sheet_name in enumerate(sheet_names): - sheet_codename = 'sheet%d.xml' % (i + 1) - worksheet_path = '%s/%s' % (PACKAGE_WORKSHEETS, sheet_codename) - - if not use_iterators: - new_ws = read_worksheet(archive.read(worksheet_path), wb, sheet_name, string_table, style_table) - else: - xml_source = unpack_worksheet(archive, worksheet_path) - new_ws = read_worksheet(xml_source, wb, sheet_name, string_table, style_table, filename, sheet_codename) - #new_ws = read_worksheet(archive.read(worksheet_path), wb, sheet_name, string_table, style_table, filename, sheet_codename) - wb.add_sheet(new_ws, index = i) - - wb._named_ranges = read_named_ranges(archive.read(ARC_WORKBOOK), wb) diff --git a/tablib/packages/openpyxl/reader/iter_worksheet.py b/tablib/packages/openpyxl/reader/iter_worksheet.py deleted file mode 100644 index 46ee3186..00000000 --- a/tablib/packages/openpyxl/reader/iter_worksheet.py +++ /dev/null @@ -1,348 +0,0 @@ -# file openpyxl/reader/iter_worksheet.py - -# Copyright (c) 2010 openpyxl -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -# @license: http://www.opensource.org/licenses/mit-license.php -# @author: Eric Gazoni - -""" Iterators-based worksheet reader -*Still very raw* -""" - -from ....compat import BytesIO as StringIO -import warnings -import operator -from functools import partial -from itertools import groupby, ifilter -from ..worksheet import Worksheet -from ..cell import coordinate_from_string, get_column_letter, Cell -from ..reader.excel import get_sheet_ids -from ..reader.strings import read_string_table -from ..reader.style import read_style_table, NumberFormat -from ..shared.date_time import SharedDate -from ..reader.worksheet import read_dimension -from ..shared.ooxml import (MIN_COLUMN, MAX_COLUMN, PACKAGE_WORKSHEETS, - MAX_ROW, MIN_ROW, ARC_SHARED_STRINGS, ARC_APP, ARC_STYLE) -try: - from xml.etree.cElementTree import iterparse -except ImportError: - from xml.etree.ElementTree import iterparse - - -from zipfile import ZipFile -from .. import cell -import re -import tempfile -import zlib -import zipfile -import struct - -TYPE_NULL = Cell.TYPE_NULL -MISSING_VALUE = None - -RE_COORDINATE = re.compile('^([A-Z]+)([0-9]+)$') - -SHARED_DATE = SharedDate() - -_COL_CONVERSION_CACHE = dict((get_column_letter(i), i) for i in xrange(1, 18279)) -def column_index_from_string(str_col, _col_conversion_cache=_COL_CONVERSION_CACHE): - # we use a function argument to get indexed name lookup - return _col_conversion_cache[str_col] -del _COL_CONVERSION_CACHE - -RAW_ATTRIBUTES = ['row', 'column', 'coordinate', 'internal_value', 'data_type', 'style_id', 'number_format'] - -try: - from collections import namedtuple - BaseRawCell = namedtuple('RawCell', RAW_ATTRIBUTES) -except ImportError: - - # warnings.warn("""Unable to import 'namedtuple' module, this may cause memory issues when using optimized reader. Please upgrade your Python installation to 2.6+""") - - class BaseRawCell(object): - - def __init__(self, *args): - assert len(args)==len(RAW_ATTRIBUTES) - - for attr, val in zip(RAW_ATTRIBUTES, args): - setattr(self, attr, val) - - def _replace(self, **kwargs): - - self.__dict__.update(kwargs) - - return self - - -class RawCell(BaseRawCell): - """Optimized version of the :class:`openpyxl.cell.Cell`, using named tuples. - - Useful attributes are: - - * row - * column - * coordinate - * internal_value - - You can also access if needed: - - * data_type - * number_format - - """ - - @property - def is_date(self): - res = (self.data_type == Cell.TYPE_NUMERIC - and self.number_format is not None - and ('d' in self.number_format - or 'm' in self.number_format - or 'y' in self.number_format - or 'h' in self.number_format - or 's' in self.number_format - )) - - return res - -def iter_rows(workbook_name, sheet_name, xml_source, range_string = '', row_offset = 0, column_offset = 0): - - archive = get_archive_file(workbook_name) - - source = xml_source - - if range_string: - min_col, min_row, max_col, max_row = get_range_boundaries(range_string, row_offset, column_offset) - else: - min_col, min_row, max_col, max_row = read_dimension(xml_source = source) - min_col = column_index_from_string(min_col) - max_col = column_index_from_string(max_col) + 1 - max_row += 6 - - try: - string_table = read_string_table(archive.read(ARC_SHARED_STRINGS)) - except KeyError: - string_table = {} - - style_table = read_style_table(archive.read(ARC_STYLE)) - - source.seek(0) - p = iterparse(source) - - return get_squared_range(p, min_col, min_row, max_col, max_row, string_table, style_table) - - -def get_rows(p, min_column = MIN_COLUMN, min_row = MIN_ROW, max_column = MAX_COLUMN, max_row = MAX_ROW): - - return groupby(get_cells(p, min_row, min_column, max_row, max_column), operator.attrgetter('row')) - -def get_cells(p, min_row, min_col, max_row, max_col, _re_coordinate=RE_COORDINATE): - - for _event, element in p: - - if element.tag == '{http://schemas.openxmlformats.org/spreadsheetml/2006/main}c': - coord = element.get('r') - column_str, row = _re_coordinate.match(coord).groups() - - row = int(row) - column = column_index_from_string(column_str) - - if min_col <= column <= max_col and min_row <= row <= max_row: - data_type = element.get('t', 'n') - style_id = element.get('s') - value = element.findtext('{http://schemas.openxmlformats.org/spreadsheetml/2006/main}v') - yield RawCell(row, column_str, coord, value, data_type, style_id, None) - - if element.tag == '{http://schemas.openxmlformats.org/spreadsheetml/2006/main}v': - continue - element.clear() - - - -def get_range_boundaries(range_string, row = 0, column = 0): - - if ':' in range_string: - min_range, max_range = range_string.split(':') - min_col, min_row = coordinate_from_string(min_range) - max_col, max_row = coordinate_from_string(max_range) - - min_col = column_index_from_string(min_col) + column - max_col = column_index_from_string(max_col) + column - min_row += row - max_row += row - - else: - min_col, min_row = coordinate_from_string(range_string) - min_col = column_index_from_string(min_col) - max_col = min_col + 1 - max_row = min_row - - return (min_col, min_row, max_col, max_row) - -def get_archive_file(archive_name): - - return ZipFile(archive_name, 'r') - -def get_xml_source(archive_file, sheet_name): - - return archive_file.read('%s/%s' % (PACKAGE_WORKSHEETS, sheet_name)) - -def get_missing_cells(row, columns): - - return dict([(column, RawCell(row, column, '%s%s' % (column, row), MISSING_VALUE, TYPE_NULL, None, None)) for column in columns]) - -def get_squared_range(p, min_col, min_row, max_col, max_row, string_table, style_table): - - expected_columns = [get_column_letter(ci) for ci in xrange(min_col, max_col)] - - current_row = min_row - for row, cells in get_rows(p, min_row = min_row, max_row = max_row, min_column = min_col, max_column = max_col): - full_row = [] - if current_row < row: - - for gap_row in xrange(current_row, row): - - dummy_cells = get_missing_cells(gap_row, expected_columns) - - yield tuple([dummy_cells[column] for column in expected_columns]) - - current_row = row - - temp_cells = list(cells) - - retrieved_columns = dict([(c.column, c) for c in temp_cells]) - - missing_columns = list(set(expected_columns) - set(retrieved_columns.keys())) - - replacement_columns = get_missing_cells(row, missing_columns) - - for column in expected_columns: - - if column in retrieved_columns: - cell = retrieved_columns[column] - - if cell.style_id is not None: - style = style_table[int(cell.style_id)] - cell = cell._replace(number_format = style.number_format.format_code) #pylint: disable-msg=W0212 - if cell.internal_value is not None: - if cell.data_type == Cell.TYPE_STRING: - cell = cell._replace(internal_value = string_table[int(cell.internal_value)]) #pylint: disable-msg=W0212 - elif cell.data_type == Cell.TYPE_BOOL: - cell = cell._replace(internal_value = cell.internal_value == 'True') - elif cell.is_date: - cell = cell._replace(internal_value = SHARED_DATE.from_julian(float(cell.internal_value))) - elif cell.data_type == Cell.TYPE_NUMERIC: - cell = cell._replace(internal_value = float(cell.internal_value)) - full_row.append(cell) - - else: - full_row.append(replacement_columns[column]) - - current_row = row + 1 - - yield tuple(full_row) - -#------------------------------------------------------------------------------ - -class IterableWorksheet(Worksheet): - - def __init__(self, parent_workbook, title, workbook_name, - sheet_codename, xml_source): - - Worksheet.__init__(self, parent_workbook, title) - self._workbook_name = workbook_name - self._sheet_codename = sheet_codename - self._xml_source = xml_source - - def iter_rows(self, range_string = '', row_offset = 0, column_offset = 0): - """ Returns a squared range based on the `range_string` parameter, - using generators. - - :param range_string: range of cells (e.g. 'A1:C4') - :type range_string: string - - :param row: row index of the cell (e.g. 4) - :type row: int - - :param column: column index of the cell (e.g. 3) - :type column: int - - :rtype: generator - - """ - - return iter_rows(workbook_name = self._workbook_name, - sheet_name = self._sheet_codename, - xml_source = self._xml_source, - range_string = range_string, - row_offset = row_offset, - column_offset = column_offset) - - def cell(self, *args, **kwargs): - - raise NotImplementedError("use 'iter_rows()' instead") - - def range(self, *args, **kwargs): - - raise NotImplementedError("use 'iter_rows()' instead") - -def unpack_worksheet(archive, filename): - - temp_file = tempfile.TemporaryFile(mode='r+', prefix='openpyxl.', suffix='.unpack.temp') - - zinfo = archive.getinfo(filename) - - if zinfo.compress_type == zipfile.ZIP_STORED: - decoder = None - elif zinfo.compress_type == zipfile.ZIP_DEFLATED: - decoder = zlib.decompressobj(-zlib.MAX_WBITS) - else: - raise zipfile.BadZipFile("Unrecognized compression method") - - archive.fp.seek(_get_file_offset(archive, zinfo)) - bytes_to_read = zinfo.compress_size - - while True: - buff = archive.fp.read(min(bytes_to_read, 102400)) - if not buff: - break - bytes_to_read -= len(buff) - if decoder: - buff = decoder.decompress(buff) - temp_file.write(buff) - - if decoder: - temp_file.write(decoder.decompress('Z')) - - return temp_file - -def _get_file_offset(archive, zinfo): - - try: - return zinfo.file_offset - except AttributeError: - # From http://stackoverflow.com/questions/3781261/how-to-simulate-zipfile-open-in-python-2-5 - - # Seek over the fixed size fields to the "file name length" field in - # the file header (26 bytes). Unpack this and the "extra field length" - # field ourselves as info.extra doesn't seem to be the correct length. - archive.fp.seek(zinfo.header_offset + 26) - file_name_len, extra_len = struct.unpack(" 10000: - msg = 'Year not supported by Excel: %s' % year - raise ValueError(msg) - if self.excel_base_date == self.CALENDAR_WINDOWS_1900: - # Fudge factor for the erroneous fact that the year 1900 is - # treated as a Leap Year in MS Excel. This affects every date - # following 28th February 1900 - if year == 1900 and month <= 2: - excel_1900_leap_year = False - else: - excel_1900_leap_year = True - excel_base_date = 2415020 - else: - raise NotImplementedError('Mac dates are not yet supported.') - #excel_base_date = 2416481 - #excel_1900_leap_year = False - - # Julian base date adjustment - if month > 2: - month = month - 3 - else: - month = month + 9 - year -= 1 - - # Calculate the Julian Date, then subtract the Excel base date - # JD 2415020 = 31 - Dec - 1899 -> Excel Date of 0 - century, decade = int(str(year)[:2]), int(str(year)[2:]) - excel_date = floor(146097 * century / 4) + \ - floor((1461 * decade) / 4) + floor((153 * month + 2) / 5) + \ - day + 1721119 - excel_base_date - if excel_1900_leap_year: - excel_date += 1 - - # check to ensure that we exclude 2/29/1900 as a possible value - if self.excel_base_date == self.CALENDAR_WINDOWS_1900 \ - and excel_date == 60: - msg = 'Error: Excel believes 1900 was a leap year' - raise ValueError(msg) - excel_time = ((hours * 3600) + (minutes * 60) + seconds) / 86400 - return excel_date + excel_time - - def from_julian(self, value=0): - """Convert from the Excel JD back to a date""" - if self.excel_base_date == self.CALENDAR_WINDOWS_1900: - excel_base_date = 25569 - if value < 60: - excel_base_date -= 1 - elif value == 60: - msg = 'Error: Excel believes 1900 was a leap year' - raise ValueError(msg) - else: - raise NotImplementedError('Mac dates are not yet supported.') - #excel_base_date = 24107 - - if value >= 1: - utc_days = value - excel_base_date - - return EPOCH + datetime.timedelta(days=utc_days) - - elif value >= 0: - hours = floor(value * 24) - mins = floor(value * 24 * 60) - floor(hours * 60) - secs = floor(value * 24 * 60 * 60) - floor(hours * 60 * 60) - \ - floor(mins * 60) - return datetime.time(int(hours), int(mins), int(secs)) - else: - msg = 'Negative dates (%s) are not supported' % value - raise ValueError(msg) diff --git a/tablib/packages/openpyxl/shared/exc.py b/tablib/packages/openpyxl/shared/exc.py deleted file mode 100644 index 94a3e2c8..00000000 --- a/tablib/packages/openpyxl/shared/exc.py +++ /dev/null @@ -1,59 +0,0 @@ -# file openpyxl/shared/exc.py - -# Copyright (c) 2010 openpyxl -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -# @license: http://www.opensource.org/licenses/mit-license.php -# @author: Eric Gazoni - -"""Definitions for openpyxl shared exception classes.""" - - -class CellCoordinatesException(Exception): - """Error for converting between numeric and A1-style cell references.""" - -class ColumnStringIndexException(Exception): - """Error for bad column names in A1-style cell references.""" - -class DataTypeException(Exception): - """Error for any data type inconsistencies.""" - -class NamedRangeException(Exception): - """Error for badly formatted named ranges.""" - -class SheetTitleException(Exception): - """Error for bad sheet names.""" - -class InsufficientCoordinatesException(Exception): - """Error for partially specified cell coordinates.""" - -class OpenModeError(Exception): - """Error for fileobj opened in non-binary mode.""" - -class InvalidFileException(Exception): - """Error for trying to open a non-ooxml file.""" - -class ReadOnlyWorkbookException(Exception): - """Error for trying to modify a read-only workbook""" - -class MissingNumberFormat(Exception): - """Error when a referenced number format is not in the stylesheet""" - - diff --git a/tablib/packages/openpyxl/shared/ooxml.py b/tablib/packages/openpyxl/shared/ooxml.py deleted file mode 100644 index 979b1724..00000000 --- a/tablib/packages/openpyxl/shared/ooxml.py +++ /dev/null @@ -1,60 +0,0 @@ -# file openpyxl/shared/ooxml.py - -# Copyright (c) 2010 openpyxl -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -# @license: http://www.opensource.org/licenses/mit-license.php -# @author: Eric Gazoni - -"""Constants for fixed paths in a file and xml namespace urls.""" - -MIN_ROW = 0 -MIN_COLUMN = 0 -MAX_COLUMN = 16384 -MAX_ROW = 1048576 - -# constants -PACKAGE_PROPS = 'docProps' -PACKAGE_XL = 'xl' -PACKAGE_RELS = '_rels' -PACKAGE_THEME = PACKAGE_XL + '/' + 'theme' -PACKAGE_WORKSHEETS = PACKAGE_XL + '/' + 'worksheets' -PACKAGE_DRAWINGS = PACKAGE_XL + '/' + 'drawings' -PACKAGE_CHARTS = PACKAGE_XL + '/' + 'charts' - -ARC_CONTENT_TYPES = '[Content_Types].xml' -ARC_ROOT_RELS = PACKAGE_RELS + '/.rels' -ARC_WORKBOOK_RELS = PACKAGE_XL + '/' + PACKAGE_RELS + '/workbook.xml.rels' -ARC_CORE = PACKAGE_PROPS + '/core.xml' -ARC_APP = PACKAGE_PROPS + '/app.xml' -ARC_WORKBOOK = PACKAGE_XL + '/workbook.xml' -ARC_STYLE = PACKAGE_XL + '/styles.xml' -ARC_THEME = PACKAGE_THEME + '/theme1.xml' -ARC_SHARED_STRINGS = PACKAGE_XL + '/sharedStrings.xml' - -NAMESPACES = { - 'cp': 'http://schemas.openxmlformats.org/package/2006/metadata/core-properties', - 'dc': 'http://purl.org/dc/elements/1.1/', - 'dcterms': 'http://purl.org/dc/terms/', - 'dcmitype': 'http://purl.org/dc/dcmitype/', - 'xsi': 'http://www.w3.org/2001/XMLSchema-instance', - 'vt': 'http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes', - 'xml': 'http://www.w3.org/XML/1998/namespace' -} diff --git a/tablib/packages/openpyxl/shared/password_hasher.py b/tablib/packages/openpyxl/shared/password_hasher.py deleted file mode 100644 index b5d0dd09..00000000 --- a/tablib/packages/openpyxl/shared/password_hasher.py +++ /dev/null @@ -1,47 +0,0 @@ -# file openpyxl/shared/password_hasher.py - -# Copyright (c) 2010 openpyxl -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -# @license: http://www.opensource.org/licenses/mit-license.php -# @author: Eric Gazoni - -"""Basic password hashing.""" - - -def hash_password(plaintext_password=''): - """Create a password hash from a given string. - - This method is based on the algorithm provided by - Daniel Rentz of OpenOffice and the PEAR package - Spreadsheet_Excel_Writer by Xavier Noguer . - - """ - password = 0x0000 - i = 1 - for char in plaintext_password: - value = ord(char) << i - rotated_bits = value >> 15 - value &= 0x7fff - password ^= (value | rotated_bits) - i += 1 - password ^= len(plaintext_password) - password ^= 0xCE4B - return str(hex(password)).upper()[2:] diff --git a/tablib/packages/openpyxl/shared/units.py b/tablib/packages/openpyxl/shared/units.py deleted file mode 100644 index fba82d70..00000000 --- a/tablib/packages/openpyxl/shared/units.py +++ /dev/null @@ -1,67 +0,0 @@ -# file openpyxl/shared/units.py - -# Copyright (c) 2010 openpyxl -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -# @license: http://www.opensource.org/licenses/mit-license.php -# @author: Eric Gazoni - -import math - -def pixels_to_EMU(value): - return int(round(value * 9525)) - -def EMU_to_pixels(value): - if not value: - return 0 - else: - return round(value / 9525.) - -def EMU_to_cm(value): - if not value: - return 0 - else: - return (EMU_to_pixels(value) * 2.57 / 96) - -def pixels_to_points(value): - return value * 0.67777777 - -def points_to_pixels(value): - if not value: - return 0 - else: - return int(math.ceil(value * 1.333333333)) - -def degrees_to_angle(value): - return int(round(value * 60000)) - -def angle_to_degrees(value): - if not value: - return 0 - else: - return round(value / 60000.) - -def short_color(color): - """ format a color to its short size """ - - if len(color) > 6: - return color[2:] - else: - return color diff --git a/tablib/packages/openpyxl/shared/xmltools.py b/tablib/packages/openpyxl/shared/xmltools.py deleted file mode 100644 index 74729e9d..00000000 --- a/tablib/packages/openpyxl/shared/xmltools.py +++ /dev/null @@ -1,114 +0,0 @@ -# file openpyxl/shared/xmltools.py - -# Copyright (c) 2010 openpyxl -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -# @license: http://www.opensource.org/licenses/mit-license.php -# @author: Eric Gazoni - -"""Shared xml tools. - -Shortcut functions taken from: - http://lethain.com/entry/2009/jan/22/handling-very-large-csv-and-xml-files-in-python/ - -""" - -# Python stdlib imports -from xml.sax.xmlreader import AttributesNSImpl -from xml.sax.saxutils import XMLGenerator -try: - from xml.etree.ElementTree import ElementTree, Element, SubElement, \ - QName, fromstring, tostring -except ImportError: - from cElementTree import ElementTree, Element, SubElement, \ - QName, fromstring, tostring - -# package imports -from .. import __name__ as prefix - - -def get_document_content(xml_node): - """Print nicely formatted xml to a string.""" - pretty_indent(xml_node) - return tostring(xml_node, 'utf-8') - - -def pretty_indent(elem, level=0): - """Format xml with nice indents and line breaks.""" - i = "\n" + level * " " - if len(elem): - if not elem.text or not elem.text.strip(): - elem.text = i + " " - if not elem.tail or not elem.tail.strip(): - elem.tail = i - for elem in elem: - pretty_indent(elem, level + 1) - if not elem.tail or not elem.tail.strip(): - elem.tail = i - else: - if level and (not elem.tail or not elem.tail.strip()): - elem.tail = i - - -def start_tag(doc, name, attr=None, body=None, namespace=None): - """Wrapper to start an xml tag.""" - if attr is None: - attr = {} - - - # name = bytes(name, 'utf-8') - - # if namespace is not None: - # namespace = bytes(namespace, 'utf-8') - - - attr_vals = {} - attr_keys = {} - for key, val in attr.items(): - - - # if key is not None: - # key = bytes(key, 'utf-8') - - # if val is not None: - # val = bytes(val, 'utf-8') - - key_tuple = (namespace, key) - - attr_vals[key_tuple] = val - attr_keys[key_tuple] = key - - attr2 = AttributesNSImpl(attr_vals, attr_keys) - doc.startElementNS((namespace, name), name, attr2) - if body: - doc.characters(body) - - -def end_tag(doc, name, namespace=None): - """Wrapper to close an xml tag.""" - doc.endElementNS((namespace, name), name) - - -def tag(doc, name, attr=None, body=None, namespace=None): - """Wrapper to print xml tags and comments.""" - if attr is None: - attr = {} - start_tag(doc, name, attr, body, namespace) - end_tag(doc, name, namespace) diff --git a/tablib/packages/openpyxl/style.py b/tablib/packages/openpyxl/style.py deleted file mode 100644 index 38628db9..00000000 --- a/tablib/packages/openpyxl/style.py +++ /dev/null @@ -1,392 +0,0 @@ -# file openpyxl/style.py - -# Copyright (c) 2010 openpyxl -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -# @license: http://www.opensource.org/licenses/mit-license.php -# @author: Eric Gazoni - -"""Style and formatting option tracking.""" - -# Python stdlib imports -import re -try: - from hashlib import md5 -except ImportError: - from md5 import md5 - - -class HashableObject(object): - """Define how to hash property classes.""" - __fields__ = None - __leaf__ = False - - def __repr__(self): - - return ':'.join([repr(getattr(self, x)) for x in self.__fields__]) - - def __hash__(self): - -# return int(md5(repr(self)).hexdigest(), 16) - return hash(repr(self)) - -class Color(HashableObject): - """Named colors for use in styles.""" - BLACK = 'FF000000' - WHITE = 'FFFFFFFF' - RED = 'FFFF0000' - DARKRED = 'FF800000' - BLUE = 'FF0000FF' - DARKBLUE = 'FF000080' - GREEN = 'FF00FF00' - DARKGREEN = 'FF008000' - YELLOW = 'FFFFFF00' - DARKYELLOW = 'FF808000' - - __fields__ = ('index',) - __slots__ = __fields__ - __leaf__ = True - - def __init__(self, index): - super(Color, self).__init__() - self.index = index - - -class Font(HashableObject): - """Font options used in styles.""" - UNDERLINE_NONE = 'none' - UNDERLINE_DOUBLE = 'double' - UNDERLINE_DOUBLE_ACCOUNTING = 'doubleAccounting' - UNDERLINE_SINGLE = 'single' - UNDERLINE_SINGLE_ACCOUNTING = 'singleAccounting' - - __fields__ = ('name', - 'size', - 'bold', - 'italic', - 'superscript', - 'subscript', - 'underline', - 'strikethrough', - 'color') - __slots__ = __fields__ - - def __init__(self): - super(Font, self).__init__() - self.name = 'Calibri' - self.size = 11 - self.bold = False - self.italic = False - self.superscript = False - self.subscript = False - self.underline = self.UNDERLINE_NONE - self.strikethrough = False - self.color = Color(Color.BLACK) - - -class Fill(HashableObject): - """Area fill patterns for use in styles.""" - FILL_NONE = 'none' - FILL_SOLID = 'solid' - FILL_GRADIENT_LINEAR = 'linear' - FILL_GRADIENT_PATH = 'path' - FILL_PATTERN_DARKDOWN = 'darkDown' - FILL_PATTERN_DARKGRAY = 'darkGray' - FILL_PATTERN_DARKGRID = 'darkGrid' - FILL_PATTERN_DARKHORIZONTAL = 'darkHorizontal' - FILL_PATTERN_DARKTRELLIS = 'darkTrellis' - FILL_PATTERN_DARKUP = 'darkUp' - FILL_PATTERN_DARKVERTICAL = 'darkVertical' - FILL_PATTERN_GRAY0625 = 'gray0625' - FILL_PATTERN_GRAY125 = 'gray125' - FILL_PATTERN_LIGHTDOWN = 'lightDown' - FILL_PATTERN_LIGHTGRAY = 'lightGray' - FILL_PATTERN_LIGHTGRID = 'lightGrid' - FILL_PATTERN_LIGHTHORIZONTAL = 'lightHorizontal' - FILL_PATTERN_LIGHTTRELLIS = 'lightTrellis' - FILL_PATTERN_LIGHTUP = 'lightUp' - FILL_PATTERN_LIGHTVERTICAL = 'lightVertical' - FILL_PATTERN_MEDIUMGRAY = 'mediumGray' - - __fields__ = ('fill_type', - 'rotation', - 'start_color', - 'end_color') - __slots__ = __fields__ - - def __init__(self): - super(Fill, self).__init__() - self.fill_type = self.FILL_NONE - self.rotation = 0 - self.start_color = Color(Color.WHITE) - self.end_color = Color(Color.BLACK) - - -class Border(HashableObject): - """Border options for use in styles.""" - BORDER_NONE = 'none' - BORDER_DASHDOT = 'dashDot' - BORDER_DASHDOTDOT = 'dashDotDot' - BORDER_DASHED = 'dashed' - BORDER_DOTTED = 'dotted' - BORDER_DOUBLE = 'double' - BORDER_HAIR = 'hair' - BORDER_MEDIUM = 'medium' - BORDER_MEDIUMDASHDOT = 'mediumDashDot' - BORDER_MEDIUMDASHDOTDOT = 'mediumDashDotDot' - BORDER_MEDIUMDASHED = 'mediumDashed' - BORDER_SLANTDASHDOT = 'slantDashDot' - BORDER_THICK = 'thick' - BORDER_THIN = 'thin' - - __fields__ = ('border_style', - 'color') - __slots__ = __fields__ - - def __init__(self): - super(Border, self).__init__() - self.border_style = self.BORDER_NONE - self.color = Color(Color.BLACK) - - -class Borders(HashableObject): - """Border positioning for use in styles.""" - DIAGONAL_NONE = 0 - DIAGONAL_UP = 1 - DIAGONAL_DOWN = 2 - DIAGONAL_BOTH = 3 - - __fields__ = ('left', - 'right', - 'top', - 'bottom', - 'diagonal', - 'diagonal_direction', - 'all_borders', - 'outline', - 'inside', - 'vertical', - 'horizontal') - __slots__ = __fields__ - - def __init__(self): - super(Borders, self).__init__() - self.left = Border() - self.right = Border() - self.top = Border() - self.bottom = Border() - self.diagonal = Border() - self.diagonal_direction = self.DIAGONAL_NONE - - self.all_borders = Border() - self.outline = Border() - self.inside = Border() - self.vertical = Border() - self.horizontal = Border() - - -class Alignment(HashableObject): - """Alignment options for use in styles.""" - HORIZONTAL_GENERAL = 'general' - HORIZONTAL_LEFT = 'left' - HORIZONTAL_RIGHT = 'right' - HORIZONTAL_CENTER = 'center' - HORIZONTAL_CENTER_CONTINUOUS = 'centerContinuous' - HORIZONTAL_JUSTIFY = 'justify' - VERTICAL_BOTTOM = 'bottom' - VERTICAL_TOP = 'top' - VERTICAL_CENTER = 'center' - VERTICAL_JUSTIFY = 'justify' - - __fields__ = ('horizontal', - 'vertical', - 'text_rotation', - 'wrap_text', - 'shrink_to_fit', - 'indent') - __slots__ = __fields__ - __leaf__ = True - - def __init__(self): - super(Alignment, self).__init__() - self.horizontal = self.HORIZONTAL_GENERAL - self.vertical = self.VERTICAL_BOTTOM - self.text_rotation = 0 - self.wrap_text = False - self.shrink_to_fit = False - self.indent = 0 - - -class NumberFormat(HashableObject): - """Numer formatting for use in styles.""" - FORMAT_GENERAL = 'General' - FORMAT_TEXT = '@' - FORMAT_NUMBER = '0' - FORMAT_NUMBER_00 = '0.00' - FORMAT_NUMBER_COMMA_SEPARATED1 = '#,##0.00' - FORMAT_NUMBER_COMMA_SEPARATED2 = '#,##0.00_-' - FORMAT_PERCENTAGE = '0%' - FORMAT_PERCENTAGE_00 = '0.00%' - FORMAT_DATE_YYYYMMDD2 = 'yyyy-mm-dd' - FORMAT_DATE_YYYYMMDD = 'yy-mm-dd' - FORMAT_DATE_DDMMYYYY = 'dd/mm/yy' - FORMAT_DATE_DMYSLASH = 'd/m/y' - FORMAT_DATE_DMYMINUS = 'd-m-y' - FORMAT_DATE_DMMINUS = 'd-m' - FORMAT_DATE_MYMINUS = 'm-y' - FORMAT_DATE_XLSX14 = 'mm-dd-yy' - FORMAT_DATE_XLSX15 = 'd-mmm-yy' - FORMAT_DATE_XLSX16 = 'd-mmm' - FORMAT_DATE_XLSX17 = 'mmm-yy' - FORMAT_DATE_XLSX22 = 'm/d/yy h:mm' - FORMAT_DATE_DATETIME = 'd/m/y h:mm' - FORMAT_DATE_TIME1 = 'h:mm AM/PM' - FORMAT_DATE_TIME2 = 'h:mm:ss AM/PM' - FORMAT_DATE_TIME3 = 'h:mm' - FORMAT_DATE_TIME4 = 'h:mm:ss' - FORMAT_DATE_TIME5 = 'mm:ss' - FORMAT_DATE_TIME6 = 'h:mm:ss' - FORMAT_DATE_TIME7 = 'i:s.S' - FORMAT_DATE_TIME8 = 'h:mm:ss@' - FORMAT_DATE_YYYYMMDDSLASH = 'yy/mm/dd@' - FORMAT_CURRENCY_USD_SIMPLE = '"$"#,##0.00_-' - FORMAT_CURRENCY_USD = '$#,##0_-' - FORMAT_CURRENCY_EUR_SIMPLE = '[$EUR ]#,##0.00_-' - _BUILTIN_FORMATS = { - 0: 'General', - 1: '0', - 2: '0.00', - 3: '#,##0', - 4: '#,##0.00', - - 9: '0%', - 10: '0.00%', - 11: '0.00E+00', - 12: '# ?/?', - 13: '# ??/??', - 14: 'mm-dd-yy', - 15: 'd-mmm-yy', - 16: 'd-mmm', - 17: 'mmm-yy', - 18: 'h:mm AM/PM', - 19: 'h:mm:ss AM/PM', - 20: 'h:mm', - 21: 'h:mm:ss', - 22: 'm/d/yy h:mm', - - 37: '#,##0 (#,##0)', - 38: '#,##0 [Red](#,##0)', - 39: '#,##0.00(#,##0.00)', - 40: '#,##0.00[Red](#,##0.00)', - - 41: '_(* #,##0_);_(* \(#,##0\);_(* "-"_);_(@_)', - 42: '_("$"* #,##0_);_("$"* \(#,##0\);_("$"* "-"_);_(@_)', - 43: '_(* #,##0.00_);_(* \(#,##0.00\);_(* "-"??_);_(@_)', - - 44: '_("$"* #,##0.00_)_("$"* \(#,##0.00\)_("$"* "-"??_)_(@_)', - 45: 'mm:ss', - 46: '[h]:mm:ss', - 47: 'mmss.0', - 48: '##0.0E+0', - 49: '@', } - _BUILTIN_FORMATS_REVERSE = dict( - [(value, key) for key, value in _BUILTIN_FORMATS.items()]) - - __fields__ = ('_format_code', - '_format_index') - __slots__ = __fields__ - __leaf__ = True - - DATE_INDICATORS = 'dmyhs' - - def __init__(self): - super(NumberFormat, self).__init__() - self._format_code = self.FORMAT_GENERAL - self._format_index = 0 - - def _set_format_code(self, format_code = FORMAT_GENERAL): - """Setter for the format_code property.""" - self._format_code = format_code - self._format_index = self.builtin_format_id(format = format_code) - - def _get_format_code(self): - """Getter for the format_code property.""" - return self._format_code - - format_code = property(_get_format_code, _set_format_code) - - def builtin_format_code(self, index): - """Return one of the standard format codes by index.""" - return self._BUILTIN_FORMATS[index] - - def is_builtin(self, format = None): - """Check if a format code is a standard format code.""" - if format is None: - format = self._format_code - return format in self._BUILTIN_FORMATS.values() - - def builtin_format_id(self, format): - """Return the id of a standard style.""" - return self._BUILTIN_FORMATS_REVERSE.get(format, None) - - def is_date_format(self, format = None): - """Check if the number format is actually representing a date.""" - if format is None: - format = self._format_code - - return any([x in format for x in self.DATE_INDICATORS]) - -class Protection(HashableObject): - """Protection options for use in styles.""" - PROTECTION_INHERIT = 'inherit' - PROTECTION_PROTECTED = 'protected' - PROTECTION_UNPROTECTED = 'unprotected' - - __fields__ = ('locked', - 'hidden') - __slots__ = __fields__ - __leaf__ = True - - def __init__(self): - super(Protection, self).__init__() - self.locked = self.PROTECTION_INHERIT - self.hidden = self.PROTECTION_INHERIT - - -class Style(HashableObject): - """Style object containing all formatting details.""" - __fields__ = ('font', - 'fill', - 'borders', - 'alignment', - 'number_format', - 'protection') - __slots__ = __fields__ - - def __init__(self): - super(Style, self).__init__() - self.font = Font() - self.fill = Fill() - self.borders = Borders() - self.alignment = Alignment() - self.number_format = NumberFormat() - self.protection = Protection() - -DEFAULTS = Style() diff --git a/tablib/packages/openpyxl/workbook.py b/tablib/packages/openpyxl/workbook.py deleted file mode 100644 index bbb14b62..00000000 --- a/tablib/packages/openpyxl/workbook.py +++ /dev/null @@ -1,186 +0,0 @@ -# file openpyxl/workbook.py - -# Copyright (c) 2010 openpyxl -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -# @license: http://www.opensource.org/licenses/mit-license.php -# @author: Eric Gazoni - -"""Workbook is the top-level container for all document information.""" - -__docformat__ = "restructuredtext en" - -# Python stdlib imports -import datetime -import os - -# package imports -from .worksheet import Worksheet -from .writer.dump_worksheet import DumpWorksheet, save_dump -from .writer.strings import StringTableBuilder -from .namedrange import NamedRange -from .style import Style -from .writer.excel import save_workbook -from .shared.exc import ReadOnlyWorkbookException - - -class DocumentProperties(object): - """High-level properties of the document.""" - - def __init__(self): - self.creator = 'Unknown' - self.last_modified_by = self.creator - self.created = datetime.datetime.now() - self.modified = datetime.datetime.now() - self.title = 'Untitled' - self.subject = '' - self.description = '' - self.keywords = '' - self.category = '' - self.company = 'Microsoft Corporation' - - -class DocumentSecurity(object): - """Security information about the document.""" - - def __init__(self): - self.lock_revision = False - self.lock_structure = False - self.lock_windows = False - self.revision_password = '' - self.workbook_password = '' - - -class Workbook(object): - """Workbook is the container for all other parts of the document.""" - - def __init__(self, optimized_write = False): - self.worksheets = [] - self._active_sheet_index = 0 - self._named_ranges = [] - self.properties = DocumentProperties() - self.style = Style() - self.security = DocumentSecurity() - self.__optimized_write = optimized_write - self.__optimized_read = False - self.strings_table_builder = StringTableBuilder() - - if not optimized_write: - self.worksheets.append(Worksheet(self)) - - def _set_optimized_read(self): - self.__optimized_read = True - - def get_active_sheet(self): - """Returns the current active sheet.""" - return self.worksheets[self._active_sheet_index] - - def create_sheet(self, index = None): - """Create a worksheet (at an optional index). - - :param index: optional position at which the sheet will be inserted - :type index: int - - """ - - if self.__optimized_read: - raise ReadOnlyWorkbookException('Cannot create new sheet in a read-only workbook') - - if self.__optimized_write : - new_ws = DumpWorksheet(parent_workbook = self) - else: - new_ws = Worksheet(parent_workbook = self) - - self.add_sheet(worksheet = new_ws, index = index) - return new_ws - - def add_sheet(self, worksheet, index = None): - """Add an existing worksheet (at an optional index).""" - if index is None: - index = len(self.worksheets) - self.worksheets.insert(index, worksheet) - - def remove_sheet(self, worksheet): - """Remove a worksheet from this workbook.""" - self.worksheets.remove(worksheet) - - def get_sheet_by_name(self, name): - """Returns a worksheet by its name. - - Returns None if no worksheet has the name specified. - - :param name: the name of the worksheet to look for - :type name: string - - """ - requested_sheet = None - for sheet in self.worksheets: - if sheet.title == name: - requested_sheet = sheet - break - return requested_sheet - - def get_index(self, worksheet): - """Return the index of the worksheet.""" - return self.worksheets.index(worksheet) - - def get_sheet_names(self): - """Returns the list of the names of worksheets in the workbook. - - Names are returned in the worksheets order. - - :rtype: list of strings - - """ - return [s.title for s in self.worksheets] - - def create_named_range(self, name, worksheet, range): - """Create a new named_range on a worksheet""" - assert isinstance(worksheet, Worksheet) - named_range = NamedRange(name, [(worksheet, range)]) - self.add_named_range(named_range) - - def get_named_ranges(self): - """Return all named ranges""" - return self._named_ranges - - def add_named_range(self, named_range): - """Add an existing named_range to the list of named_ranges.""" - self._named_ranges.append(named_range) - - def get_named_range(self, name): - """Return the range specified by name.""" - requested_range = None - for named_range in self._named_ranges: - if named_range.name == name: - requested_range = named_range - break - return requested_range - - def remove_named_range(self, named_range): - """Remove a named_range from this workbook.""" - self._named_ranges.remove(named_range) - - def save(self, filename): - """ shortcut """ - if self.__optimized_write: - save_dump(self, filename) - else: - save_workbook(self, filename) diff --git a/tablib/packages/openpyxl/worksheet.py b/tablib/packages/openpyxl/worksheet.py deleted file mode 100644 index 4f3955cf..00000000 --- a/tablib/packages/openpyxl/worksheet.py +++ /dev/null @@ -1,534 +0,0 @@ -# file openpyxl/worksheet.py - -# Copyright (c) 2010 openpyxl -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -# @license: http://www.opensource.org/licenses/mit-license.php -# @author: Eric Gazoni - -"""Worksheet is the 2nd-level container in Excel.""" - -# Python stdlib imports -import re - -# package imports -from . import cell -from .cell import coordinate_from_string, \ - column_index_from_string, get_column_letter -from .shared.exc import SheetTitleException, \ - InsufficientCoordinatesException, CellCoordinatesException, \ - NamedRangeException -from .shared.password_hasher import hash_password -from .style import Style, DEFAULTS as DEFAULTS_STYLE -from .drawing import Drawing - -_DEFAULTS_STYLE_HASH = hash(DEFAULTS_STYLE) - -def flatten(results): - - rows = [] - - for row in results: - - cells = [] - - for cell in row: - - cells.append(cell.value) - - rows.append(tuple(cells)) - - return tuple(rows) - - -class Relationship(object): - """Represents many kinds of relationships.""" - # TODO: Use this object for workbook relationships as well as - # worksheet relationships - TYPES = { - 'hyperlink': 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink', - 'drawing':'http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing', - #'worksheet': 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet', - #'sharedStrings': 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings', - #'styles': 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles', - #'theme': 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme', - } - - def __init__(self, rel_type): - if rel_type not in self.TYPES: - raise ValueError("Invalid relationship type %s" % rel_type) - self.type = self.TYPES[rel_type] - self.target = "" - self.target_mode = "" - self.id = "" - - -class PageSetup(object): - """Information about page layout for this sheet""" - pass - - -class HeaderFooter(object): - """Information about the header/footer for this sheet.""" - pass - - -class SheetView(object): - """Information about the visible portions of this sheet.""" - pass - - -class RowDimension(object): - """Information about the display properties of a row.""" - __slots__ = ('row_index', - 'height', - 'visible', - 'outline_level', - 'collapsed', - 'style_index',) - - def __init__(self, index = 0): - self.row_index = index - self.height = -1 - self.visible = True - self.outline_level = 0 - self.collapsed = False - self.style_index = None - - -class ColumnDimension(object): - """Information about the display properties of a column.""" - __slots__ = ('column_index', - 'width', - 'auto_size', - 'visible', - 'outline_level', - 'collapsed', - 'style_index',) - - def __init__(self, index = 'A'): - self.column_index = index - self.width = -1 - self.auto_size = False - self.visible = True - self.outline_level = 0 - self.collapsed = False - self.style_index = 0 - - -class PageMargins(object): - """Information about page margins for view/print layouts.""" - - def __init__(self): - self.left = self.right = 0.7 - self.top = self.bottom = 0.75 - self.header = self.footer = 0.3 - - -class SheetProtection(object): - """Information about protection of various aspects of a sheet.""" - - def __init__(self): - self.sheet = False - self.objects = False - self.scenarios = False - self.format_cells = False - self.format_columns = False - self.format_rows = False - self.insert_columns = False - self.insert_rows = False - self.insert_hyperlinks = False - self.delete_columns = False - self.delete_rows = False - self.select_locked_cells = False - self.sort = False - self.auto_filter = False - self.pivot_tables = False - self.select_unlocked_cells = False - self._password = '' - - def set_password(self, value = '', already_hashed = False): - """Set a password on this sheet.""" - if not already_hashed: - value = hash_password(value) - self._password = value - - def _set_raw_password(self, value): - """Set a password directly, forcing a hash step.""" - self.set_password(value, already_hashed = False) - - def _get_raw_password(self): - """Return the password value, regardless of hash.""" - return self._password - - password = property(_get_raw_password, _set_raw_password, - 'get/set the password (if already hashed, ' - 'use set_password() instead)') - - -class Worksheet(object): - """Represents a worksheet. - - Do not create worksheets yourself, - use :func:`openpyxl.workbook.Workbook.create_sheet` instead - - """ - BREAK_NONE = 0 - BREAK_ROW = 1 - BREAK_COLUMN = 2 - - SHEETSTATE_VISIBLE = 'visible' - SHEETSTATE_HIDDEN = 'hidden' - SHEETSTATE_VERYHIDDEN = 'veryHidden' - - def __init__(self, parent_workbook, title = 'Sheet'): - self._parent = parent_workbook - self._title = '' - if not title: - self.title = 'Sheet%d' % (1 + len(self._parent.worksheets)) - else: - self.title = title - self.row_dimensions = {} - self.column_dimensions = {} - self._cells = {} - self._styles = {} - self._charts = [] - self.relationships = [] - self.selected_cell = 'A1' - self.active_cell = 'A1' - self.sheet_state = self.SHEETSTATE_VISIBLE - self.page_setup = PageSetup() - self.page_margins = PageMargins() - self.header_footer = HeaderFooter() - self.sheet_view = SheetView() - self.protection = SheetProtection() - self.show_gridlines = True - self.print_gridlines = False - self.show_summary_below = True - self.show_summary_right = True - self.default_row_dimension = RowDimension() - self.default_column_dimension = ColumnDimension() - self._auto_filter = None - self._freeze_panes = None - - def __repr__(self): - return '' % self.title - - def garbage_collect(self): - """Delete cells that are not storing a value.""" - delete_list = [coordinate for coordinate, cell in \ - self._cells.items() if (cell.value in ('', None) and \ - hash(cell.style) == _DEFAULTS_STYLE_HASH)] - for coordinate in delete_list: - del self._cells[coordinate] - - def get_cell_collection(self): - """Return an unordered list of the cells in this worksheet.""" - return self._cells.values() - - def _set_title(self, value): - """Set a sheet title, ensuring it is valid.""" - bad_title_char_re = re.compile(r'[\\*?:/\[\]]') - if bad_title_char_re.search(value): - msg = 'Invalid character found in sheet title' - raise SheetTitleException(msg) - - # check if sheet_name already exists - # do this *before* length check - if self._parent.get_sheet_by_name(value): - # use name, but append with lowest possible integer - i = 1 - while self._parent.get_sheet_by_name('%s%d' % (value, i)): - i += 1 - value = '%s%d' % (value, i) - if len(value) > 31: - msg = 'Maximum 31 characters allowed in sheet title' - raise SheetTitleException(msg) - self._title = value - - def _get_title(self): - """Return the title for this sheet.""" - return self._title - - title = property(_get_title, _set_title, doc = - 'Get or set the title of the worksheet. ' - 'Limited to 31 characters, no special characters.') - - def _set_auto_filter(self, range): - # Normalize range to a str or None - if not range: - range = None - elif isinstance(range, str): - range = range.upper() - else: # Assume a range - range = range[0][0].address + ':' + range[-1][-1].address - self._auto_filter = range - - def _get_auto_filter(self): - return self._auto_filter - - auto_filter = property(_get_auto_filter, _set_auto_filter, doc = - 'get or set auto filtering on columns') - def _set_freeze_panes(self, topLeftCell): - if not topLeftCell: - topLeftCell = None - elif isinstance(topLeftCell, str): - topLeftCell = topLeftCell.upper() - else: # Assume a cell - topLeftCell = topLeftCell.address - if topLeftCell == 'A1': - topLeftCell = None - self._freeze_panes = topLeftCell - - def _get_freeze_panes(self): - return self._freeze_panes - - freeze_panes = property(_get_freeze_panes,_set_freeze_panes, doc = - "Get or set frozen panes") - - def cell(self, coordinate = None, row = None, column = None): - """Returns a cell object based on the given coordinates. - - Usage: cell(coodinate='A15') **or** cell(row=15, column=1) - - If `coordinates` are not given, then row *and* column must be given. - - Cells are kept in a dictionary which is empty at the worksheet - creation. Calling `cell` creates the cell in memory when they - are first accessed, to reduce memory usage. - - :param coordinate: coordinates of the cell (e.g. 'B12') - :type coordinate: string - - :param row: row index of the cell (e.g. 4) - :type row: int - - :param column: column index of the cell (e.g. 3) - :type column: int - - :raise: InsufficientCoordinatesException when coordinate or (row and column) are not given - - :rtype: :class:`openpyxl.cell.Cell` - - """ - if not coordinate: - if (row is None or column is None): - msg = "You have to provide a value either for " \ - "'coordinate' or for 'row' *and* 'column'" - raise InsufficientCoordinatesException(msg) - else: - coordinate = '%s%s' % (get_column_letter(column + 1), row + 1) - else: - coordinate = coordinate.replace('$', '') - - return self._get_cell(coordinate) - - def _get_cell(self, coordinate): - - if not coordinate in self._cells: - column, row = coordinate_from_string(coordinate) - new_cell = cell.Cell(self, column, row) - self._cells[coordinate] = new_cell - if column not in self.column_dimensions: - self.column_dimensions[column] = ColumnDimension(column) - if row not in self.row_dimensions: - self.row_dimensions[row] = RowDimension(row) - return self._cells[coordinate] - - def get_highest_row(self): - """Returns the maximum row index containing data - - :rtype: int - """ - if self.row_dimensions: - return max(self.row_dimensions.keys()) - else: - return 1 - - def get_highest_column(self): - """Get the largest value for column currently stored. - - :rtype: int - """ - if self.column_dimensions: - return max([column_index_from_string(column_index) - for column_index in self.column_dimensions]) - else: - return 1 - - def calculate_dimension(self): - """Return the minimum bounding range for all cells containing data.""" - return 'A1:%s%d' % (get_column_letter(self.get_highest_column()), - self.get_highest_row()) - - def range(self, range_string, row = 0, column = 0): - """Returns a 2D array of cells, with optional row and column offsets. - - :param range_string: cell range string or `named range` name - :type range_string: string - - :param row: number of rows to offset - :type row: int - - :param column: number of columns to offset - :type column: int - - :rtype: tuples of tuples of :class:`openpyxl.cell.Cell` - - """ - if ':' in range_string: - # R1C1 range - result = [] - min_range, max_range = range_string.split(':') - min_col, min_row = coordinate_from_string(min_range) - max_col, max_row = coordinate_from_string(max_range) - if column: - min_col = get_column_letter( - column_index_from_string(min_col) + column) - max_col = get_column_letter( - column_index_from_string(max_col) + column) - min_col = column_index_from_string(min_col) - max_col = column_index_from_string(max_col) - cache_cols = {} - for col in xrange(min_col, max_col + 1): - cache_cols[col] = get_column_letter(col) - rows = xrange(min_row + row, max_row + row + 1) - cols = xrange(min_col, max_col + 1) - for row in rows: - new_row = [] - for col in cols: - new_row.append(self.cell('%s%s' % (cache_cols[col], row))) - result.append(tuple(new_row)) - return tuple(result) - else: - try: - return self.cell(coordinate = range_string, row = row, - column = column) - except CellCoordinatesException: - pass - - # named range - named_range = self._parent.get_named_range(range_string) - if named_range is None: - msg = '%s is not a valid range name' % range_string - raise NamedRangeException(msg) - - result = [] - for destination in named_range.destinations: - - worksheet, cells_range = destination - - if worksheet is not self: - msg = 'Range %s is not defined on worksheet %s' % \ - (cells_range, self.title) - raise NamedRangeException(msg) - - content = self.range(cells_range) - - if isinstance(content, tuple): - for cells in content: - result.extend(cells) - else: - result.append(content) - - if len(result) == 1: - return result[0] - else: - return tuple(result) - - def get_style(self, coordinate): - """Return the style object for the specified cell.""" - if not coordinate in self._styles: - self._styles[coordinate] = Style() - return self._styles[coordinate] - - def create_relationship(self, rel_type): - """Add a relationship for this sheet.""" - rel = Relationship(rel_type) - self.relationships.append(rel) - rel_id = self.relationships.index(rel) - rel.id = 'rId' + str(rel_id + 1) - return self.relationships[rel_id] - - def add_chart(self, chart): - """ Add a chart to the sheet """ - - chart._sheet = self - self._charts.append(chart) - - def append(self, list_or_dict): - """Appends a group of values at the bottom of the current sheet. - - * If it's a list: all values are added in order, starting from the first column - * If it's a dict: values are assigned to the columns indicated by the keys (numbers or letters) - - :param list_or_dict: list or dict containing values to append - :type list_or_dict: list/tuple or dict - - Usage: - - * append(['This is A1', 'This is B1', 'This is C1']) - * **or** append({'A' : 'This is A1', 'C' : 'This is C1'}) - * **or** append({0 : 'This is A1', 2 : 'This is C1'}) - - :raise: TypeError when list_or_dict is neither a list/tuple nor a dict - - """ - - row_idx = len(self.row_dimensions) - - if isinstance(list_or_dict, (list, tuple)): - - for col_idx, content in enumerate(list_or_dict): - - self.cell(row = row_idx, column = col_idx).value = content - - elif isinstance(list_or_dict, dict): - - for col_idx, content in list_or_dict.items(): - - if isinstance(col_idx, basestring): - col_idx = column_index_from_string(col_idx) - 1 - - self.cell(row = row_idx, column = col_idx).value = content - - else: - raise TypeError('list_or_dict must be a list or a dict') - - @property - def rows(self): - - return self.range(self.calculate_dimension()) - - @property - def columns(self): - - max_row = self.get_highest_row() - - cols = [] - - for col_idx in range(self.get_highest_column()): - col = get_column_letter(col_idx+1) - res = self.range('%s1:%s%d' % (col, col, max_row)) - cols.append(tuple([x[0] for x in res])) - - - return tuple(cols) - diff --git a/tablib/packages/openpyxl/writer/__init__.py b/tablib/packages/openpyxl/writer/__init__.py deleted file mode 100644 index 9eb0a218..00000000 --- a/tablib/packages/openpyxl/writer/__init__.py +++ /dev/null @@ -1,34 +0,0 @@ -# file openpyxl/writer/__init__.py - -# Copyright (c) 2010 openpyxl -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -# @license: http://www.opensource.org/licenses/mit-license.php -# @author: Eric Gazoni - -"""Imports for the openpyxl.writer namespace.""" - -# package imports -from . import excel -from . import strings -from . import styles -from . import theme -from . import workbook -from . import worksheet diff --git a/tablib/packages/openpyxl/writer/charts.py b/tablib/packages/openpyxl/writer/charts.py deleted file mode 100644 index 2c8df396..00000000 --- a/tablib/packages/openpyxl/writer/charts.py +++ /dev/null @@ -1,261 +0,0 @@ -# coding=UTF-8 -''' -Copyright (c) 2010 openpyxl - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - -@license: http://www.opensource.org/licenses/mit-license.php -@author: Eric Gazoni -''' - -from ..shared.xmltools import Element, SubElement, get_document_content -from ..chart import Chart, ErrorBar - -class ChartWriter(object): - - def __init__(self, chart): - self.chart = chart - - def write(self): - """ write a chart """ - - root = Element('c:chartSpace', - {'xmlns:c':"http://schemas.openxmlformats.org/drawingml/2006/chart", - 'xmlns:a':"http://schemas.openxmlformats.org/drawingml/2006/main", - 'xmlns:r':"http://schemas.openxmlformats.org/officeDocument/2006/relationships"}) - - SubElement(root, 'c:lang', {'val':self.chart.lang}) - self._write_chart(root) - self._write_print_settings(root) - self._write_shapes(root) - - return get_document_content(root) - - def _write_chart(self, root): - - chart = self.chart - - ch = SubElement(root, 'c:chart') - self._write_title(ch) - plot_area = SubElement(ch, 'c:plotArea') - layout = SubElement(plot_area, 'c:layout') - mlayout = SubElement(layout, 'c:manualLayout') - SubElement(mlayout, 'c:layoutTarget', {'val':'inner'}) - SubElement(mlayout, 'c:xMode', {'val':'edge'}) - SubElement(mlayout, 'c:yMode', {'val':'edge'}) - SubElement(mlayout, 'c:x', {'val':str(chart._get_margin_left())}) - SubElement(mlayout, 'c:y', {'val':str(chart._get_margin_top())}) - SubElement(mlayout, 'c:w', {'val':str(chart.width)}) - SubElement(mlayout, 'c:h', {'val':str(chart.height)}) - - if chart.type == Chart.SCATTER_CHART: - subchart = SubElement(plot_area, 'c:scatterChart') - SubElement(subchart, 'c:scatterStyle', {'val':str('lineMarker')}) - else: - if chart.type == Chart.BAR_CHART: - subchart = SubElement(plot_area, 'c:barChart') - SubElement(subchart, 'c:barDir', {'val':'col'}) - else: - subchart = SubElement(plot_area, 'c:lineChart') - - SubElement(subchart, 'c:grouping', {'val':chart.grouping}) - - self._write_series(subchart) - - SubElement(subchart, 'c:marker', {'val':'1'}) - SubElement(subchart, 'c:axId', {'val':str(chart.x_axis.id)}) - SubElement(subchart, 'c:axId', {'val':str(chart.y_axis.id)}) - - if chart.type == Chart.SCATTER_CHART: - self._write_axis(plot_area, chart.x_axis, 'c:valAx') - else: - self._write_axis(plot_area, chart.x_axis, 'c:catAx') - self._write_axis(plot_area, chart.y_axis, 'c:valAx') - - self._write_legend(ch) - - SubElement(ch, 'c:plotVisOnly', {'val':'1'}) - - def _write_title(self, chart): - if self.chart.title != '': - title = SubElement(chart, 'c:title') - tx = SubElement(title, 'c:tx') - rich = SubElement(tx, 'c:rich') - SubElement(rich, 'a:bodyPr') - SubElement(rich, 'a:lstStyle') - p = SubElement(rich, 'a:p') - pPr = SubElement(p, 'a:pPr') - SubElement(pPr, 'a:defRPr') - r = SubElement(p, 'a:r') - SubElement(r, 'a:rPr', {'lang':self.chart.lang}) - t = SubElement(r, 'a:t').text = self.chart.title - SubElement(title, 'c:layout') - - def _write_axis(self, plot_area, axis, label): - - ax = SubElement(plot_area, label) - SubElement(ax, 'c:axId', {'val':str(axis.id)}) - - scaling = SubElement(ax, 'c:scaling') - SubElement(scaling, 'c:orientation', {'val':axis.orientation}) - if label == 'c:valAx': - SubElement(scaling, 'c:max', {'val':str(axis.max)}) - SubElement(scaling, 'c:min', {'val':str(axis.min)}) - - SubElement(ax, 'c:axPos', {'val':axis.position}) - if label == 'c:valAx': - SubElement(ax, 'c:majorGridlines') - SubElement(ax, 'c:numFmt', {'formatCode':"General", 'sourceLinked':'1'}) - SubElement(ax, 'c:tickLblPos', {'val':axis.tick_label_position}) - SubElement(ax, 'c:crossAx', {'val':str(axis.cross)}) - SubElement(ax, 'c:crosses', {'val':axis.crosses}) - if axis.auto: - SubElement(ax, 'c:auto', {'val':'1'}) - if axis.label_align: - SubElement(ax, 'c:lblAlgn', {'val':axis.label_align}) - if axis.label_offset: - SubElement(ax, 'c:lblOffset', {'val':str(axis.label_offset)}) - if label == 'c:valAx': - if self.chart.type == Chart.SCATTER_CHART: - SubElement(ax, 'c:crossBetween', {'val':'midCat'}) - else: - SubElement(ax, 'c:crossBetween', {'val':'between'}) - SubElement(ax, 'c:majorUnit', {'val':str(axis.unit)}) - - def _write_series(self, subchart): - - for i, serie in enumerate(self.chart._series): - ser = SubElement(subchart, 'c:ser') - SubElement(ser, 'c:idx', {'val':str(i)}) - SubElement(ser, 'c:order', {'val':str(i)}) - - if serie.legend: - tx = SubElement(ser, 'c:tx') - self._write_serial(tx, serie.legend) - - if serie.color: - sppr = SubElement(ser, 'c:spPr') - if self.chart.type == Chart.BAR_CHART: - # fill color - fillc = SubElement(sppr, 'a:solidFill') - SubElement(fillc, 'a:srgbClr', {'val':serie.color}) - # edge color - ln = SubElement(sppr, 'a:ln') - fill = SubElement(ln, 'a:solidFill') - SubElement(fill, 'a:srgbClr', {'val':serie.color}) - - if serie.error_bar: - self._write_error_bar(ser, serie) - - marker = SubElement(ser, 'c:marker') - SubElement(marker, 'c:symbol', {'val':serie.marker}) - - if serie.labels: - cat = SubElement(ser, 'c:cat') - self._write_serial(cat, serie.labels) - - if self.chart.type == Chart.SCATTER_CHART: - if serie.xvalues: - xval = SubElement(ser, 'c:xVal') - self._write_serial(xval, serie.xvalues) - - yval = SubElement(ser, 'c:yVal') - self._write_serial(yval, serie.values) - else: - val = SubElement(ser, 'c:val') - self._write_serial(val, serie.values) - - def _write_serial(self, node, serie, literal=False): - - cache = serie._get_cache() - if isinstance(cache[0], basestring): - typ = 'str' - else: - typ = 'num' - - if not literal: - if typ == 'num': - ref = SubElement(node, 'c:numRef') - else: - ref = SubElement(node, 'c:strRef') - SubElement(ref, 'c:f').text = serie._get_ref() - if typ == 'num': - data = SubElement(ref, 'c:numCache') - else: - data = SubElement(ref, 'c:strCache') - else: - data = SubElement(node, 'c:numLit') - - if typ == 'num': - SubElement(data, 'c:formatCode').text = 'General' - if literal: - values = (1,) - else: - values = cache - - SubElement(data, 'c:ptCount', {'val':str(len(values))}) - for j, val in enumerate(values): - point = SubElement(data, 'c:pt', {'idx':str(j)}) - SubElement(point, 'c:v').text = str(val) - - def _write_error_bar(self, node, serie): - - flag = {ErrorBar.PLUS_MINUS:'both', - ErrorBar.PLUS:'plus', - ErrorBar.MINUS:'minus'} - - eb = SubElement(node, 'c:errBars') - SubElement(eb, 'c:errBarType', {'val':flag[serie.error_bar.type]}) - SubElement(eb, 'c:errValType', {'val':'cust'}) - - plus = SubElement(eb, 'c:plus') - self._write_serial(plus, serie.error_bar.values, - literal=(serie.error_bar.type==ErrorBar.MINUS)) - - minus = SubElement(eb, 'c:minus') - self._write_serial(minus, serie.error_bar.values, - literal=(serie.error_bar.type==ErrorBar.PLUS)) - - def _write_legend(self, chart): - - legend = SubElement(chart, 'c:legend') - SubElement(legend, 'c:legendPos', {'val':self.chart.legend.position}) - SubElement(legend, 'c:layout') - - def _write_print_settings(self, root): - - settings = SubElement(root, 'c:printSettings') - SubElement(settings, 'c:headerFooter') - margins = dict([(k, str(v)) for (k,v) in self.chart.print_margins.items()]) - SubElement(settings, 'c:pageMargins', margins) - SubElement(settings, 'c:pageSetup') - - def _write_shapes(self, root): - - if self.chart._shapes: - SubElement(root, 'c:userShapes', {'r:id':'rId1'}) - - def write_rels(self, drawing_id): - - root = Element('Relationships', {'xmlns' : 'http://schemas.openxmlformats.org/package/2006/relationships'}) - attrs = {'Id' : 'rId1', - 'Type' : 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/chartUserShapes', - 'Target' : '../drawings/drawing%s.xml' % drawing_id } - SubElement(root, 'Relationship', attrs) - return get_document_content(root) diff --git a/tablib/packages/openpyxl/writer/drawings.py b/tablib/packages/openpyxl/writer/drawings.py deleted file mode 100644 index 8a6cce21..00000000 --- a/tablib/packages/openpyxl/writer/drawings.py +++ /dev/null @@ -1,192 +0,0 @@ -# coding=UTF-8 -''' -Copyright (c) 2010 openpyxl - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - -@license: http://www.opensource.org/licenses/mit-license.php -@author: Eric Gazoni -''' - -from ..shared.xmltools import Element, SubElement, get_document_content - - -class DrawingWriter(object): - """ one main drawing file per sheet """ - - def __init__(self, sheet): - self._sheet = sheet - - def write(self): - """ write drawings for one sheet in one file """ - - root = Element('xdr:wsDr', - {'xmlns:xdr' : "http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing", - 'xmlns:a' : "http://schemas.openxmlformats.org/drawingml/2006/main"}) - - for i, chart in enumerate(self._sheet._charts): - - drawing = chart.drawing - -# anchor = SubElement(root, 'xdr:twoCellAnchor') -# (start_row, start_col), (end_row, end_col) = drawing.coordinates -# # anchor coordinates -# _from = SubElement(anchor, 'xdr:from') -# x = SubElement(_from, 'xdr:col').text = str(start_col) -# x = SubElement(_from, 'xdr:colOff').text = '0' -# x = SubElement(_from, 'xdr:row').text = str(start_row) -# x = SubElement(_from, 'xdr:rowOff').text = '0' - -# _to = SubElement(anchor, 'xdr:to') -# x = SubElement(_to, 'xdr:col').text = str(end_col) -# x = SubElement(_to, 'xdr:colOff').text = '0' -# x = SubElement(_to, 'xdr:row').text = str(end_row) -# x = SubElement(_to, 'xdr:rowOff').text = '0' - - # we only support absolute anchor atm (TODO: oneCellAnchor, twoCellAnchor - x, y, w, h = drawing.get_emu_dimensions() - anchor = SubElement(root, 'xdr:absoluteAnchor') - SubElement(anchor, 'xdr:pos', {'x':str(x), 'y':str(y)}) - SubElement(anchor, 'xdr:ext', {'cx':str(w), 'cy':str(h)}) - - # graph frame - frame = SubElement(anchor, 'xdr:graphicFrame', {'macro':''}) - - name = SubElement(frame, 'xdr:nvGraphicFramePr') - SubElement(name, 'xdr:cNvPr', {'id':'%s' % i, 'name':'Graphique %s' % i}) - SubElement(name, 'xdr:cNvGraphicFramePr') - - frm = SubElement(frame, 'xdr:xfrm') - # no transformation - SubElement(frm, 'a:off', {'x':'0', 'y':'0'}) - SubElement(frm, 'a:ext', {'cx':'0', 'cy':'0'}) - - graph = SubElement(frame, 'a:graphic') - data = SubElement(graph, 'a:graphicData', - {'uri':'http://schemas.openxmlformats.org/drawingml/2006/chart'}) - SubElement(data, 'c:chart', - { 'xmlns:c':'http://schemas.openxmlformats.org/drawingml/2006/chart', - 'xmlns:r':'http://schemas.openxmlformats.org/officeDocument/2006/relationships', - 'r:id':'rId%s' % (i + 1)}) - - SubElement(anchor, 'xdr:clientData') - - return get_document_content(root) - - def write_rels(self, chart_id): - - root = Element('Relationships', - {'xmlns' : 'http://schemas.openxmlformats.org/package/2006/relationships'}) - for i, chart in enumerate(self._sheet._charts): - attrs = {'Id' : 'rId%s' % (i + 1), - 'Type' : 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart', - 'Target' : '../charts/chart%s.xml' % (chart_id + i) } - SubElement(root, 'Relationship', attrs) - return get_document_content(root) - -class ShapeWriter(object): - """ one file per shape """ - - schema = "http://schemas.openxmlformats.org/drawingml/2006/main" - - def __init__(self, shapes): - - self._shapes = shapes - - def write(self, shape_id): - - root = Element('c:userShapes', {'xmlns:c' : 'http://schemas.openxmlformats.org/drawingml/2006/chart'}) - - for shape in self._shapes: - anchor = SubElement(root, 'cdr:relSizeAnchor', - {'xmlns:cdr' : "http://schemas.openxmlformats.org/drawingml/2006/chartDrawing"}) - - xstart, ystart, xend, yend = shape.get_coordinates() - - _from = SubElement(anchor, 'cdr:from') - SubElement(_from, 'cdr:x').text = str(xstart) - SubElement(_from, 'cdr:y').text = str(ystart) - - _to = SubElement(anchor, 'cdr:to') - SubElement(_to, 'cdr:x').text = str(xend) - SubElement(_to, 'cdr:y').text = str(yend) - - sp = SubElement(anchor, 'cdr:sp', {'macro':'', 'textlink':''}) - nvspr = SubElement(sp, 'cdr:nvSpPr') - SubElement(nvspr, 'cdr:cNvPr', {'id':str(shape_id), 'name':'shape %s' % shape_id}) - SubElement(nvspr, 'cdr:cNvSpPr') - - sppr = SubElement(sp, 'cdr:spPr') - frm = SubElement(sppr, 'a:xfrm', {'xmlns:a':self.schema}) - # no transformation - SubElement(frm, 'a:off', {'x':'0', 'y':'0'}) - SubElement(frm, 'a:ext', {'cx':'0', 'cy':'0'}) - - prstgeom = SubElement(sppr, 'a:prstGeom', {'xmlns:a':self.schema, 'prst':str(shape.style)}) - SubElement(prstgeom, 'a:avLst') - - fill = SubElement(sppr, 'a:solidFill', {'xmlns:a':self.schema}) - SubElement(fill, 'a:srgbClr', {'val':shape.color}) - - border = SubElement(sppr, 'a:ln', {'xmlns:a':self.schema, 'w':str(shape._border_width)}) - sf = SubElement(border, 'a:solidFill') - SubElement(sf, 'a:srgbClr', {'val':shape.border_color}) - - self._write_style(sp) - self._write_text(sp, shape) - - shape_id += 1 - - return get_document_content(root) - - def _write_text(self, node, shape): - """ write text in the shape """ - - tx_body = SubElement(node, 'cdr:txBody') - SubElement(tx_body, 'a:bodyPr', {'xmlns:a':self.schema, 'vertOverflow':'clip'}) - SubElement(tx_body, 'a:lstStyle', - {'xmlns:a':self.schema}) - p = SubElement(tx_body, 'a:p', {'xmlns:a':self.schema}) - if shape.text: - r = SubElement(p, 'a:r') - rpr = SubElement(r, 'a:rPr', {'lang':'en-US'}) - fill = SubElement(rpr, 'a:solidFill') - SubElement(fill, 'a:srgbClr', {'val':shape.text_color}) - - SubElement(r, 'a:t').text = shape.text - else: - SubElement(p, 'a:endParaRPr', {'lang':'en-US'}) - - def _write_style(self, node): - """ write style theme """ - - style = SubElement(node, 'cdr:style') - - ln_ref = SubElement(style, 'a:lnRef', {'xmlns:a':self.schema, 'idx':'2'}) - scheme_clr = SubElement(ln_ref, 'a:schemeClr', {'val':'accent1'}) - SubElement(scheme_clr, 'a:shade', {'val':'50000'}) - - fill_ref = SubElement(style, 'a:fillRef', {'xmlns:a':self.schema, 'idx':'1'}) - SubElement(fill_ref, 'a:schemeClr', {'val':'accent1'}) - - effect_ref = SubElement(style, 'a:effectRef', {'xmlns:a':self.schema, 'idx':'0'}) - SubElement(effect_ref, 'a:schemeClr', {'val':'accent1'}) - - font_ref = SubElement(style, 'a:fontRef', {'xmlns:a':self.schema, 'idx':'minor'}) - SubElement(font_ref, 'a:schemeClr', {'val':'lt1'}) diff --git a/tablib/packages/openpyxl/writer/dump_worksheet.py b/tablib/packages/openpyxl/writer/dump_worksheet.py deleted file mode 100644 index 7f098f55..00000000 --- a/tablib/packages/openpyxl/writer/dump_worksheet.py +++ /dev/null @@ -1,256 +0,0 @@ -# file openpyxl/writer/straight_worksheet.py - -# Copyright (c) 2010 openpyxl -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -# @license: http://www.opensource.org/licenses/mit-license.php -# @author: Eric Gazoni - -"""Write worksheets to xml representations in an optimized way""" - -import datetime -import os - -from ..cell import column_index_from_string, get_column_letter, Cell -from ..worksheet import Worksheet -from ..shared.xmltools import XMLGenerator, get_document_content, \ - start_tag, end_tag, tag -from ..shared.date_time import SharedDate -from ..shared.ooxml import MAX_COLUMN, MAX_ROW -from tempfile import NamedTemporaryFile -from ..writer.excel import ExcelWriter -from ..writer.strings import write_string_table -from ..writer.styles import StyleWriter -from ..style import Style, NumberFormat - -from ..shared.ooxml import ARC_SHARED_STRINGS, ARC_CONTENT_TYPES, \ - ARC_ROOT_RELS, ARC_WORKBOOK_RELS, ARC_APP, ARC_CORE, ARC_THEME, \ - ARC_STYLE, ARC_WORKBOOK, \ - PACKAGE_WORKSHEETS, PACKAGE_DRAWINGS, PACKAGE_CHARTS - -STYLES = {'datetime' : {'type':Cell.TYPE_NUMERIC, - 'style':'1'}, - 'string':{'type':Cell.TYPE_STRING, - 'style':'0'}, - 'numeric':{'type':Cell.TYPE_NUMERIC, - 'style':'0'}, - 'formula':{'type':Cell.TYPE_FORMULA, - 'style':'0'}, - 'boolean':{'type':Cell.TYPE_BOOL, - 'style':'0'}, - } - -DATETIME_STYLE = Style() -DATETIME_STYLE.number_format.format_code = NumberFormat.FORMAT_DATE_YYYYMMDD2 -BOUNDING_BOX_PLACEHOLDER = 'A1:%s%d' % (get_column_letter(MAX_COLUMN), MAX_ROW) - -class DumpWorksheet(Worksheet): - - """ - .. warning:: - - You shouldn't initialize this yourself, use :class:`openpyxl.workbook.Workbook` constructor instead, - with `optimized_write = True`. - """ - - def __init__(self, parent_workbook): - - Worksheet.__init__(self, parent_workbook) - - self._max_col = 0 - self._max_row = 0 - self._parent = parent_workbook - self._fileobj_header = NamedTemporaryFile(mode='r+', prefix='openpyxl.', suffix='.header', delete=False) - self._fileobj_content = NamedTemporaryFile(mode='r+', prefix='openpyxl.', suffix='.content', delete=False) - self._fileobj = NamedTemporaryFile(mode='w', prefix='openpyxl.', delete=False) - self.doc = XMLGenerator(self._fileobj_content, 'utf-8') - self.header = XMLGenerator(self._fileobj_header, 'utf-8') - self.title = 'Sheet' - - self._shared_date = SharedDate() - self._string_builder = self._parent.strings_table_builder - - @property - def filename(self): - return self._fileobj.name - - def write_header(self): - - doc = self.header - - start_tag(doc, 'worksheet', - {'xml:space': 'preserve', - 'xmlns': 'http://schemas.openxmlformats.org/spreadsheetml/2006/main', - 'xmlns:r': 'http://schemas.openxmlformats.org/officeDocument/2006/relationships'}) - start_tag(doc, 'sheetPr') - tag(doc, 'outlinePr', - {'summaryBelow': '1', - 'summaryRight': '1'}) - end_tag(doc, 'sheetPr') - tag(doc, 'dimension', {'ref': 'A1:%s' % (self.get_dimensions())}) - start_tag(doc, 'sheetViews') - start_tag(doc, 'sheetView', {'workbookViewId': '0'}) - tag(doc, 'selection', {'activeCell': 'A1', - 'sqref': 'A1'}) - end_tag(doc, 'sheetView') - end_tag(doc, 'sheetViews') - tag(doc, 'sheetFormatPr', {'defaultRowHeight': '15'}) - start_tag(doc, 'sheetData') - - def close(self): - - self._close_content() - self._close_header() - - self._write_fileobj(self._fileobj_header) - self._write_fileobj(self._fileobj_content) - - self._fileobj.close() - - def _write_fileobj(self, fobj): - - fobj.flush() - fobj.seek(0) - - while True: - chunk = fobj.read(4096) - if not chunk: - break - self._fileobj.write(chunk) - - fobj.close() - os.remove(fobj.name) - - self._fileobj.flush() - - def _close_header(self): - - doc = self.header - #doc.endDocument() - - def _close_content(self): - - doc = self.doc - end_tag(doc, 'sheetData') - - end_tag(doc, 'worksheet') - #doc.endDocument() - - def get_dimensions(self): - - if not self._max_col or not self._max_row: - return 'A1' - else: - return '%s%d' % (get_column_letter(self._max_col), (self._max_row)) - - def append(self, row): - - """ - :param row: iterable containing values to append - :type row: iterable - """ - - doc = self.doc - - self._max_row += 1 - span = len(row) - self._max_col = max(self._max_col, span) - - row_idx = self._max_row - - attrs = {'r': '%d' % row_idx, - 'spans': '1:%d' % span} - - start_tag(doc, 'row', attrs) - - for col_idx, cell in enumerate(row): - - if cell is None: - continue - - coordinate = '%s%d' % (get_column_letter(col_idx+1), row_idx) - attributes = {'r': coordinate} - - if isinstance(cell, bool): - dtype = 'boolean' - elif isinstance(cell, (int, float)): - dtype = 'numeric' - elif isinstance(cell, (datetime.datetime, datetime.date)): - dtype = 'datetime' - cell = self._shared_date.datetime_to_julian(cell) - attributes['s'] = STYLES[dtype]['style'] - elif cell and cell[0] == '=': - dtype = 'formula' - else: - dtype = 'string' - cell = self._string_builder.add(cell) - - attributes['t'] = STYLES[dtype]['type'] - - start_tag(doc, 'c', attributes) - - if dtype == 'formula': - tag(doc, 'f', body = '%s' % cell[1:]) - tag(doc, 'v') - else: - tag(doc, 'v', body = '%s' % cell) - - end_tag(doc, 'c') - - - end_tag(doc, 'row') - - -def save_dump(workbook, filename): - - writer = ExcelDumpWriter(workbook) - writer.save(filename) - return True - -class ExcelDumpWriter(ExcelWriter): - - def __init__(self, workbook): - - self.workbook = workbook - self.style_writer = StyleDumpWriter(workbook) - self.style_writer._style_list.append(DATETIME_STYLE) - - def _write_string_table(self, archive): - - shared_string_table = self.workbook.strings_table_builder.get_table() - archive.writestr(ARC_SHARED_STRINGS, - write_string_table(shared_string_table)) - - return shared_string_table - - def _write_worksheets(self, archive, shared_string_table, style_writer): - - for i, sheet in enumerate(self.workbook.worksheets): - sheet.write_header() - sheet.close() - archive.write(sheet.filename, PACKAGE_WORKSHEETS + '/sheet%d.xml' % (i + 1)) - os.remove(sheet.filename) - - -class StyleDumpWriter(StyleWriter): - - def _get_style_list(self, workbook): - return [] - diff --git a/tablib/packages/openpyxl/writer/excel.py b/tablib/packages/openpyxl/writer/excel.py deleted file mode 100644 index b95245ec..00000000 --- a/tablib/packages/openpyxl/writer/excel.py +++ /dev/null @@ -1,161 +0,0 @@ -# file openpyxl/writer/excel.py - -# Copyright (c) 2010 openpyxl -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -# @license: http://www.opensource.org/licenses/mit-license.php -# @author: Eric Gazoni - -"""Write a .xlsx file.""" - -# Python stdlib imports -from zipfile import ZipFile, ZIP_DEFLATED -from ....compat import BytesIO as StringIO - -# package imports -from ..shared.ooxml import ARC_SHARED_STRINGS, ARC_CONTENT_TYPES, \ - ARC_ROOT_RELS, ARC_WORKBOOK_RELS, ARC_APP, ARC_CORE, ARC_THEME, \ - ARC_STYLE, ARC_WORKBOOK, \ - PACKAGE_WORKSHEETS, PACKAGE_DRAWINGS, PACKAGE_CHARTS -from ..writer.strings import create_string_table, write_string_table -from ..writer.workbook import write_content_types, write_root_rels, \ - write_workbook_rels, write_properties_app, write_properties_core, \ - write_workbook -from ..writer.theme import write_theme -from ..writer.styles import StyleWriter -from ..writer.drawings import DrawingWriter, ShapeWriter -from ..writer.charts import ChartWriter -from ..writer.worksheet import write_worksheet, write_worksheet_rels - - -class ExcelWriter(object): - """Write a workbook object to an Excel file.""" - - def __init__(self, workbook): - self.workbook = workbook - self.style_writer = StyleWriter(self.workbook) - - def write_data(self, archive): - """Write the various xml files into the zip archive.""" - # cleanup all worksheets - shared_string_table = self._write_string_table(archive) - - archive.writestr(ARC_CONTENT_TYPES, write_content_types(self.workbook)) - archive.writestr(ARC_ROOT_RELS, write_root_rels(self.workbook)) - archive.writestr(ARC_WORKBOOK_RELS, write_workbook_rels(self.workbook)) - archive.writestr(ARC_APP, write_properties_app(self.workbook)) - archive.writestr(ARC_CORE, - write_properties_core(self.workbook.properties)) - archive.writestr(ARC_THEME, write_theme()) - archive.writestr(ARC_STYLE, self.style_writer.write_table()) - archive.writestr(ARC_WORKBOOK, write_workbook(self.workbook)) - - self._write_worksheets(archive, shared_string_table, self.style_writer) - - def _write_string_table(self, archive): - - for ws in self.workbook.worksheets: - ws.garbage_collect() - shared_string_table = create_string_table(self.workbook) - - - archive.writestr(ARC_SHARED_STRINGS, - write_string_table(shared_string_table)) - - for k, v in shared_string_table.items(): - shared_string_table[k] = bytes(v) - - return shared_string_table - - def _write_worksheets(self, archive, shared_string_table, style_writer): - - drawing_id = 1 - chart_id = 1 - shape_id = 1 - - for i, sheet in enumerate(self.workbook.worksheets): - archive.writestr(PACKAGE_WORKSHEETS + '/sheet%d.xml' % (i + 1), - write_worksheet(sheet, shared_string_table, - style_writer.get_style_by_hash())) - if sheet._charts or sheet.relationships: - archive.writestr(PACKAGE_WORKSHEETS + - '/_rels/sheet%d.xml.rels' % (i + 1), - write_worksheet_rels(sheet, drawing_id)) - if sheet._charts: - dw = DrawingWriter(sheet) - archive.writestr(PACKAGE_DRAWINGS + '/drawing%d.xml' % drawing_id, - dw.write()) - archive.writestr(PACKAGE_DRAWINGS + '/_rels/drawing%d.xml.rels' % drawing_id, - dw.write_rels(chart_id)) - drawing_id += 1 - - for chart in sheet._charts: - cw = ChartWriter(chart) - archive.writestr(PACKAGE_CHARTS + '/chart%d.xml' % chart_id, - cw.write()) - - if chart._shapes: - archive.writestr(PACKAGE_CHARTS + '/_rels/chart%d.xml.rels' % chart_id, - cw.write_rels(drawing_id)) - sw = ShapeWriter(chart._shapes) - archive.writestr(PACKAGE_DRAWINGS + '/drawing%d.xml' % drawing_id, - sw.write(shape_id)) - shape_id += len(chart._shapes) - drawing_id += 1 - - chart_id += 1 - - - def save(self, filename): - """Write data into the archive.""" - archive = ZipFile(filename, 'w', ZIP_DEFLATED) - self.write_data(archive) - archive.close() - - -def save_workbook(workbook, filename): - """Save the given workbook on the filesystem under the name filename. - - :param workbook: the workbook to save - :type workbook: :class:`openpyxl.workbook.Workbook` - - :param filename: the path to which save the workbook - :type filename: string - - :rtype: bool - - """ - writer = ExcelWriter(workbook) - writer.save(filename) - return True - - -def save_virtual_workbook(workbook): - """Return an in-memory workbook, suitable for a Django response.""" - writer = ExcelWriter(workbook) - temp_buffer = StringIO() - try: - archive = ZipFile(temp_buffer, 'w', ZIP_DEFLATED) - writer.write_data(archive) - finally: - archive.close() - virtual_workbook = temp_buffer.getvalue() - temp_buffer.close() - return virtual_workbook diff --git a/tablib/packages/openpyxl/writer/strings.py b/tablib/packages/openpyxl/writer/strings.py deleted file mode 100644 index f73daed2..00000000 --- a/tablib/packages/openpyxl/writer/strings.py +++ /dev/null @@ -1,86 +0,0 @@ -# file openpyxl/writer/strings.py - -# Copyright (c) 2010 openpyxl -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -# @license: http://www.opensource.org/licenses/mit-license.php -# @author: Eric Gazoni - -"""Write the shared string table.""" - -# Python stdlib imports -from ....compat import BytesIO as StringIO - -# package imports -from ..shared.xmltools import start_tag, end_tag, tag, XMLGenerator - - -def create_string_table(workbook): - """Compile the string table for a workbook.""" - strings = set() - for sheet in workbook.worksheets: - for cell in sheet.get_cell_collection(): - if cell.data_type == cell.TYPE_STRING and cell._value is not None: - strings.add(cell.value) - return dict((key, i) for i, key in enumerate(strings)) - - -def write_string_table(string_table): - """Write the string table xml.""" - temp_buffer = StringIO() - doc = XMLGenerator(temp_buffer, 'utf-8') - start_tag(doc, 'sst', {'xmlns': - 'http://schemas.openxmlformats.org/spreadsheetml/2006/main', - 'uniqueCount': '%d' % len(string_table)}) - strings_to_write = sorted(string_table.items(), - key=lambda pair: pair[1]) - for key in [pair[0] for pair in strings_to_write]: - start_tag(doc, 'si') - if key.strip() != key: - attr = {'xml:space': 'preserve'} - else: - attr = {} - tag(doc, 't', attr, key) - end_tag(doc, 'si') - end_tag(doc, 'sst') - string_table_xml = temp_buffer.getvalue() - temp_buffer.close() - return string_table_xml - -class StringTableBuilder(object): - - def __init__(self): - - self.counter = 0 - self.dct = {} - - def add(self, key): - - key = key.strip() - try: - return self.dct[key] - except KeyError: - res = self.dct[key] = self.counter - self.counter += 1 - return res - - def get_table(self): - - return self.dct diff --git a/tablib/packages/openpyxl/writer/styles.py b/tablib/packages/openpyxl/writer/styles.py deleted file mode 100644 index 70dd7196..00000000 --- a/tablib/packages/openpyxl/writer/styles.py +++ /dev/null @@ -1,256 +0,0 @@ -# file openpyxl/writer/styles.py - -# Copyright (c) 2010 openpyxl -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -# @license: http://www.opensource.org/licenses/mit-license.php -# @author: Eric Gazoni - -"""Write the shared style table.""" - -# package imports -from ..shared.xmltools import Element, SubElement -from ..shared.xmltools import get_document_content -from .. import style - -class StyleWriter(object): - - def __init__(self, workbook): - self._style_list = self._get_style_list(workbook) - self._root = Element('styleSheet', - {'xmlns':'http://schemas.openxmlformats.org/spreadsheetml/2006/main'}) - - def _get_style_list(self, workbook): - crc = {} - for worksheet in workbook.worksheets: - for style in worksheet._styles.values(): - crc[hash(style)] = style - self.style_table = dict([(style, i+1) \ - for i, style in enumerate(crc.values())]) - sorted_styles = sorted(self.style_table.items(), \ - key = lambda pair:pair[1]) - return [s[0] for s in sorted_styles] - - def get_style_by_hash(self): - return dict([(hash(style), id) \ - for style, id in self.style_table.items()]) - - def write_table(self): - number_format_table = self._write_number_formats() - fonts_table = self._write_fonts() - fills_table = self._write_fills() - borders_table = self._write_borders() - self._write_cell_style_xfs() - self._write_cell_xfs(number_format_table, fonts_table, fills_table, borders_table) - self._write_cell_style() - self._write_dxfs() - self._write_table_styles() - - return get_document_content(xml_node=self._root) - - def _write_fonts(self): - """ add fonts part to root - return {font.crc => index} - """ - - fonts = SubElement(self._root, 'fonts') - - # default - font_node = SubElement(fonts, 'font') - SubElement(font_node, 'sz', {'val':'11'}) - SubElement(font_node, 'color', {'theme':'1'}) - SubElement(font_node, 'name', {'val':'Calibri'}) - SubElement(font_node, 'family', {'val':'2'}) - SubElement(font_node, 'scheme', {'val':'minor'}) - - # others - table = {} - index = 1 - for st in self._style_list: - if hash(st.font) != hash(style.DEFAULTS.font) and hash(st.font) not in table: - table[hash(st.font)] = str(index) - font_node = SubElement(fonts, 'font') - SubElement(font_node, 'sz', {'val':str(st.font.size)}) - SubElement(font_node, 'color', {'rgb':str(st.font.color.index)}) - SubElement(font_node, 'name', {'val':st.font.name}) - SubElement(font_node, 'family', {'val':'2'}) - SubElement(font_node, 'scheme', {'val':'minor'}) - if st.font.bold: - SubElement(font_node, 'b') - if st.font.italic: - SubElement(font_node, 'i') - index += 1 - - fonts.attrib["count"] = str(index) - return table - - def _write_fills(self): - fills = SubElement(self._root, 'fills', {'count':'2'}) - fill = SubElement(fills, 'fill') - SubElement(fill, 'patternFill', {'patternType':'none'}) - fill = SubElement(fills, 'fill') - SubElement(fill, 'patternFill', {'patternType':'gray125'}) - - table = {} - index = 2 - for st in self._style_list: - if hash(st.fill) != hash(style.DEFAULTS.fill) and hash(st.fill) not in table: - table[hash(st.fill)] = str(index) - fill = SubElement(fills, 'fill') - if hash(st.fill.fill_type) != hash(style.DEFAULTS.fill.fill_type): - node = SubElement(fill,'patternFill', {'patternType':st.fill.fill_type}) - if hash(st.fill.start_color) != hash(style.DEFAULTS.fill.start_color): - - SubElement(node, 'fgColor', {'rgb':str(st.fill.start_color.index)}) - if hash(st.fill.end_color) != hash(style.DEFAULTS.fill.end_color): - SubElement(node, 'bgColor', {'rgb':str(st.fill.start_color.index)}) - index += 1 - - fills.attrib["count"] = str(index) - return table - - def _write_borders(self): - borders = SubElement(self._root, 'borders') - - # default - border = SubElement(borders, 'border') - SubElement(border, 'left') - SubElement(border, 'right') - SubElement(border, 'top') - SubElement(border, 'bottom') - SubElement(border, 'diagonal') - - # others - table = {} - index = 1 - for st in self._style_list: - if hash(st.borders) != hash(style.DEFAULTS.borders) and hash(st.borders) not in table: - table[hash(st.borders)] = str(index) - border = SubElement(borders, 'border') - # caution: respect this order - for side in ('left','right','top','bottom','diagonal'): - obj = getattr(st.borders, side) - node = SubElement(border, side, {'style':obj.border_style}) - SubElement(node, 'color', {'rgb':str(obj.color.index)}) - index += 1 - - borders.attrib["count"] = str(index) - return table - - def _write_cell_style_xfs(self): - cell_style_xfs = SubElement(self._root, 'cellStyleXfs', {'count':'1'}) - xf = SubElement(cell_style_xfs, 'xf', - {'numFmtId':"0", 'fontId':"0", 'fillId':"0", 'borderId':"0"}) - - def _write_cell_xfs(self, number_format_table, fonts_table, fills_table, borders_table): - """ write styles combinations based on ids found in tables """ - - # writing the cellXfs - cell_xfs = SubElement(self._root, 'cellXfs', - {'count':'%d' % (len(self._style_list) + 1)}) - - # default - def _get_default_vals(): - return dict(numFmtId='0', fontId='0', fillId='0', - xfId='0', borderId='0') - - SubElement(cell_xfs, 'xf', _get_default_vals()) - - for st in self._style_list: - vals = _get_default_vals() - - if hash(st.font) != hash(style.DEFAULTS.font): - vals['fontId'] = fonts_table[hash(st.font)] - vals['applyFont'] = '1' - - if hash(st.borders) != hash(style.DEFAULTS.borders): - vals['borderId'] = borders_table[hash(st.borders)] - vals['applyBorder'] = '1' - - if hash(st.fill) != hash(style.DEFAULTS.fill): - vals['fillId'] = fills_table[hash(st.fill)] - vals['applyFillId'] = '1' - - if st.number_format != style.DEFAULTS.number_format: - vals['numFmtId'] = '%d' % number_format_table[st.number_format] - vals['applyNumberFormat'] = '1' - - if hash(st.alignment) != hash(style.DEFAULTS.alignment): - vals['applyAlignment'] = '1' - - node = SubElement(cell_xfs, 'xf', vals) - - if hash(st.alignment) != hash(style.DEFAULTS.alignment): - alignments = {} - - for align_attr in ['horizontal','vertical']: - if hash(getattr(st.alignment, align_attr)) != hash(getattr(style.DEFAULTS.alignment, align_attr)): - alignments[align_attr] = getattr(st.alignment, align_attr) - - SubElement(node, 'alignment', alignments) - - - def _write_cell_style(self): - cell_styles = SubElement(self._root, 'cellStyles', {'count':'1'}) - cell_style = SubElement(cell_styles, 'cellStyle', - {'name':"Normal", 'xfId':"0", 'builtinId':"0"}) - - def _write_dxfs(self): - dxfs = SubElement(self._root, 'dxfs', {'count':'0'}) - - def _write_table_styles(self): - - table_styles = SubElement(self._root, 'tableStyles', - {'count':'0', 'defaultTableStyle':'TableStyleMedium9', - 'defaultPivotStyle':'PivotStyleLight16'}) - - def _write_number_formats(self): - - number_format_table = {} - - number_format_list = [] - exceptions_list = [] - num_fmt_id = 165 # start at a greatly higher value as any builtin can go - num_fmt_offset = 0 - - for style in self._style_list: - - if not style.number_format in number_format_list : - number_format_list.append(style.number_format) - - for number_format in number_format_list: - - if number_format.is_builtin(): - btin = number_format.builtin_format_id(number_format.format_code) - number_format_table[number_format] = btin - else: - number_format_table[number_format] = num_fmt_id + num_fmt_offset - num_fmt_offset += 1 - exceptions_list.append(number_format) - - num_fmts = SubElement(self._root, 'numFmts', - {'count':'%d' % len(exceptions_list)}) - - for number_format in exceptions_list : - SubElement(num_fmts, 'numFmt', - {'numFmtId':'%d' % number_format_table[number_format], - 'formatCode':'%s' % number_format.format_code}) - - return number_format_table diff --git a/tablib/packages/openpyxl/writer/theme.py b/tablib/packages/openpyxl/writer/theme.py deleted file mode 100644 index 80700f2c..00000000 --- a/tablib/packages/openpyxl/writer/theme.py +++ /dev/null @@ -1,202 +0,0 @@ -# -*- coding: utf-8 -*- -# file openpyxl/writer/theme.py - -# Copyright (c) 2010 openpyxl -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -# @license: http://www.opensource.org/licenses/mit-license.php -# @author: Eric Gazoni - -"""Write the theme xml based on a fixed string.""" - -# package imports -from ..shared.xmltools import fromstring, get_document_content - - -def write_theme(): - """Write the theme xml.""" - xml_node = fromstring( - '\n' - - '' - '' - - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '') - return get_document_content(xml_node) diff --git a/tablib/packages/openpyxl/writer/workbook.py b/tablib/packages/openpyxl/writer/workbook.py deleted file mode 100644 index e7b390c1..00000000 --- a/tablib/packages/openpyxl/writer/workbook.py +++ /dev/null @@ -1,204 +0,0 @@ -# file openpyxl/writer/workbook.py - -# Copyright (c) 2010 openpyxl -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -# @license: http://www.opensource.org/licenses/mit-license.php -# @author: Eric Gazoni - -"""Write the workbook global settings to the archive.""" - -# package imports -from ..shared.xmltools import Element, SubElement -from ..cell import absolute_coordinate -from ..shared.xmltools import get_document_content -from ..shared.ooxml import NAMESPACES, ARC_CORE, ARC_WORKBOOK, \ - ARC_APP, ARC_THEME, ARC_STYLE, ARC_SHARED_STRINGS -from ..shared.date_time import datetime_to_W3CDTF - - -def write_properties_core(properties): - """Write the core properties to xml.""" - root = Element('cp:coreProperties', {'xmlns:cp': NAMESPACES['cp'], - 'xmlns:xsi': NAMESPACES['xsi'], 'xmlns:dc': NAMESPACES['dc'], - 'xmlns:dcterms': NAMESPACES['dcterms'], - 'xmlns:dcmitype': NAMESPACES['dcmitype'], }) - SubElement(root, 'dc:creator').text = properties.creator - SubElement(root, 'cp:lastModifiedBy').text = properties.last_modified_by - SubElement(root, 'dcterms:created', \ - {'xsi:type': 'dcterms:W3CDTF'}).text = \ - datetime_to_W3CDTF(properties.created) - SubElement(root, 'dcterms:modified', - {'xsi:type': 'dcterms:W3CDTF'}).text = \ - datetime_to_W3CDTF(properties.modified) - return get_document_content(root) - - -def write_content_types(workbook): - """Write the content-types xml.""" - root = Element('Types', {'xmlns': 'http://schemas.openxmlformats.org/package/2006/content-types'}) - SubElement(root, 'Override', {'PartName': '/' + ARC_THEME, 'ContentType': 'application/vnd.openxmlformats-officedocument.theme+xml'}) - SubElement(root, 'Override', {'PartName': '/' + ARC_STYLE, 'ContentType': 'application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml'}) - SubElement(root, 'Default', {'Extension': 'rels', 'ContentType': 'application/vnd.openxmlformats-package.relationships+xml'}) - SubElement(root, 'Default', {'Extension': 'xml', 'ContentType': 'application/xml'}) - SubElement(root, 'Override', {'PartName': '/' + ARC_WORKBOOK, 'ContentType': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml'}) - SubElement(root, 'Override', {'PartName': '/' + ARC_APP, 'ContentType': 'application/vnd.openxmlformats-officedocument.extended-properties+xml'}) - SubElement(root, 'Override', {'PartName': '/' + ARC_CORE, 'ContentType': 'application/vnd.openxmlformats-package.core-properties+xml'}) - SubElement(root, 'Override', {'PartName': '/' + ARC_SHARED_STRINGS, 'ContentType': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml'}) - - drawing_id = 1 - chart_id = 1 - - for sheet_id, sheet in enumerate(workbook.worksheets): - SubElement(root, 'Override', - {'PartName': '/xl/worksheets/sheet%d.xml' % (sheet_id + 1), - 'ContentType': 'application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml'}) - if sheet._charts: - SubElement(root, 'Override', - {'PartName' : '/xl/drawings/drawing%d.xml' % (sheet_id + 1), - 'ContentType' : 'application/vnd.openxmlformats-officedocument.drawing+xml'}) - drawing_id += 1 - - for chart in sheet._charts: - SubElement(root, 'Override', - {'PartName' : '/xl/charts/chart%d.xml' % chart_id, - 'ContentType' : 'application/vnd.openxmlformats-officedocument.drawingml.chart+xml'}) - chart_id += 1 - if chart._shapes: - SubElement(root, 'Override', - {'PartName' : '/xl/drawings/drawing%d.xml' % drawing_id, - 'ContentType' : 'application/vnd.openxmlformats-officedocument.drawingml.chartshapes+xml'}) - drawing_id += 1 - - return get_document_content(root) - - -def write_properties_app(workbook): - """Write the properties xml.""" - worksheets_count = len(workbook.worksheets) - root = Element('Properties', {'xmlns': 'http://schemas.openxmlformats.org/officeDocument/2006/extended-properties', - 'xmlns:vt': 'http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes'}) - SubElement(root, 'Application').text = 'Microsoft Excel' - SubElement(root, 'DocSecurity').text = '0' - SubElement(root, 'ScaleCrop').text = 'false' - SubElement(root, 'Company') - SubElement(root, 'LinksUpToDate').text = 'false' - SubElement(root, 'SharedDoc').text = 'false' - SubElement(root, 'HyperlinksChanged').text = 'false' - SubElement(root, 'AppVersion').text = '12.0000' - - # heading pairs part - heading_pairs = SubElement(root, 'HeadingPairs') - vector = SubElement(heading_pairs, 'vt:vector', - {'size': '2', 'baseType': 'variant'}) - variant = SubElement(vector, 'vt:variant') - SubElement(variant, 'vt:lpstr').text = 'Worksheets' - variant = SubElement(vector, 'vt:variant') - SubElement(variant, 'vt:i4').text = '%d' % worksheets_count - - # title of parts - title_of_parts = SubElement(root, 'TitlesOfParts') - vector = SubElement(title_of_parts, 'vt:vector', - {'size': '%d' % worksheets_count, 'baseType': 'lpstr'}) - for ws in workbook.worksheets: - SubElement(vector, 'vt:lpstr').text = '%s' % ws.title - return get_document_content(root) - - -def write_root_rels(workbook): - """Write the relationships xml.""" - root = Element('Relationships', {'xmlns': - 'http://schemas.openxmlformats.org/package/2006/relationships'}) - SubElement(root, 'Relationship', {'Id': 'rId1', 'Target': ARC_WORKBOOK, - 'Type': 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument'}) - SubElement(root, 'Relationship', {'Id': 'rId2', 'Target': ARC_CORE, - 'Type': 'http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties'}) - SubElement(root, 'Relationship', {'Id': 'rId3', 'Target': ARC_APP, - 'Type': 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties'}) - return get_document_content(root) - - -def write_workbook(workbook): - """Write the core workbook xml.""" - root = Element('workbook', {'xmlns': 'http://schemas.openxmlformats.org/spreadsheetml/2006/main', - 'xml:space': 'preserve', 'xmlns:r': 'http://schemas.openxmlformats.org/officeDocument/2006/relationships'}) - SubElement(root, 'fileVersion', {'appName': 'xl', 'lastEdited': '4', - 'lowestEdited': '4', 'rupBuild': '4505'}) - SubElement(root, 'workbookPr', {'defaultThemeVersion': '124226', - 'codeName': 'ThisWorkbook'}) - book_views = SubElement(root, 'bookViews') - SubElement(book_views, 'workbookView', {'activeTab': '%d' % workbook.get_index(workbook.get_active_sheet()), - 'autoFilterDateGrouping': '1', 'firstSheet': '0', 'minimized': '0', - 'showHorizontalScroll': '1', 'showSheetTabs': '1', - 'showVerticalScroll': '1', 'tabRatio': '600', - 'visibility': 'visible'}) - # worksheets - sheets = SubElement(root, 'sheets') - for i, sheet in enumerate(workbook.worksheets): - sheet_node = SubElement(sheets, 'sheet', {'name': sheet.title, - 'sheetId': '%d' % (i + 1), 'r:id': 'rId%d' % (i + 1)}) - if not sheet.sheet_state == sheet.SHEETSTATE_VISIBLE: - sheet_node.set('state', sheet.sheet_state) - # named ranges - defined_names = SubElement(root, 'definedNames') - for named_range in workbook.get_named_ranges(): - name = SubElement(defined_names, 'definedName', - {'name': named_range.name}) - - # as there can be many cells in one range, generate the list of ranges - dest_cells = [] - cell_ids = [] - for worksheet, range_name in named_range.destinations: - cell_ids.append(workbook.get_index(worksheet)) - dest_cells.append("'%s'!%s" % (worksheet.title.replace("'", "''"), - absolute_coordinate(range_name))) - - # for local ranges, we must check all the cells belong to the same sheet - base_id = cell_ids[0] - if named_range.local_only and all([x == base_id for x in cell_ids]): - name.set('localSheetId', '%s' % base_id) - - # finally write the cells list - name.text = ','.join(dest_cells) - - SubElement(root, 'calcPr', {'calcId': '124519', 'calcMode': 'auto', - 'fullCalcOnLoad': '1'}) - return get_document_content(root) - - -def write_workbook_rels(workbook): - """Write the workbook relationships xml.""" - root = Element('Relationships', {'xmlns': - 'http://schemas.openxmlformats.org/package/2006/relationships'}) - for i in range(len(workbook.worksheets)): - SubElement(root, 'Relationship', {'Id': 'rId%d' % (i + 1), - 'Type': 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet', - 'Target': 'worksheets/sheet%s.xml' % (i + 1)}) - rid = len(workbook.worksheets) + 1 - SubElement(root, 'Relationship', - {'Id': 'rId%d' % rid, 'Target': 'sharedStrings.xml', - 'Type': 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings'}) - SubElement(root, 'Relationship', - {'Id': 'rId%d' % (rid + 1), 'Target': 'styles.xml', - 'Type': 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles'}) - SubElement(root, 'Relationship', - {'Id': 'rId%d' % (rid + 2), 'Target': 'theme/theme1.xml', - 'Type': 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme'}) - return get_document_content(root) diff --git a/tablib/packages/openpyxl/writer/worksheet.py b/tablib/packages/openpyxl/writer/worksheet.py deleted file mode 100644 index 91effe2d..00000000 --- a/tablib/packages/openpyxl/writer/worksheet.py +++ /dev/null @@ -1,209 +0,0 @@ -# file openpyxl/writer/worksheet.py - -# Copyright (c) 2010 openpyxl -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -# @license: http://www.opensource.org/licenses/mit-license.php -# @author: Eric Gazoni - -"""Write worksheets to xml representations.""" - -# Python stdlib imports -from ....compat import BytesIO as StringIO # cStringIO doesn't handle unicode - -# package imports -from ..cell import coordinate_from_string, column_index_from_string -from ..shared.xmltools import Element, SubElement, XMLGenerator, \ - get_document_content, start_tag, end_tag, tag - - -def row_sort(cell): - """Translate column names for sorting.""" - return column_index_from_string(cell.column) - - -def write_worksheet(worksheet, string_table, style_table): - """Write a worksheet to an xml file.""" - xml_file = StringIO() - doc = XMLGenerator(xml_file, 'utf-8') - start_tag(doc, 'worksheet', - {'xml:space': 'preserve', - 'xmlns': 'http://schemas.openxmlformats.org/spreadsheetml/2006/main', - 'xmlns:r': 'http://schemas.openxmlformats.org/officeDocument/2006/relationships'}) - start_tag(doc, 'sheetPr') - tag(doc, 'outlinePr', - {'summaryBelow': '%d' % (worksheet.show_summary_below), - 'summaryRight': '%d' % (worksheet.show_summary_right)}) - end_tag(doc, 'sheetPr') - tag(doc, 'dimension', {'ref': '%s' % worksheet.calculate_dimension()}) - write_worksheet_sheetviews(doc, worksheet) - tag(doc, 'sheetFormatPr', {'defaultRowHeight': '15'}) - write_worksheet_cols(doc, worksheet) - write_worksheet_data(doc, worksheet, string_table, style_table) - if worksheet.auto_filter: - tag(doc, 'autoFilter', {'ref': worksheet.auto_filter}) - write_worksheet_hyperlinks(doc, worksheet) - if worksheet._charts: - tag(doc, 'drawing', {'r:id':'rId1'}) - end_tag(doc, 'worksheet') - doc.endDocument() - xml_string = xml_file.getvalue() - xml_file.close() - return xml_string - -def write_worksheet_sheetviews(doc, worksheet): - start_tag(doc, 'sheetViews') - start_tag(doc, 'sheetView', {'workbookViewId': '0'}) - selectionAttrs = {} - topLeftCell = worksheet.freeze_panes - if topLeftCell: - colName, row = coordinate_from_string(topLeftCell) - column = column_index_from_string(colName) - pane = 'topRight' - paneAttrs = {} - if column > 1: - paneAttrs['xSplit'] = str(column - 1) - if row > 1: - paneAttrs['ySplit'] = str(row - 1) - pane = 'bottomLeft' - if column > 1: - pane = 'bottomRight' - paneAttrs.update(dict(topLeftCell=topLeftCell, - activePane=pane, - state='frozen')) - tag(doc, 'pane', paneAttrs) - selectionAttrs['pane'] = pane - if row > 1 and column > 1: - tag(doc, 'selection', {'pane': 'topRight'}) - tag(doc, 'selection', {'pane': 'bottomLeft'}) - - selectionAttrs.update({'activeCell': worksheet.active_cell, - 'sqref': worksheet.selected_cell}) - - tag(doc, 'selection', selectionAttrs) - end_tag(doc, 'sheetView') - end_tag(doc, 'sheetViews') - - -def write_worksheet_cols(doc, worksheet): - """Write worksheet columns to xml.""" - if worksheet.column_dimensions: - start_tag(doc, 'cols') - for column_string, columndimension in \ - worksheet.column_dimensions.items(): - col_index = column_index_from_string(column_string) - col_def = {} - col_def['collapsed'] = str(columndimension.style_index) - col_def['min'] = str(col_index) - col_def['max'] = str(col_index) - if columndimension.width != \ - worksheet.default_column_dimension.width: - col_def['customWidth'] = 'true' - if not columndimension.visible: - col_def['hidden'] = 'true' - if columndimension.outline_level > 0: - col_def['outlineLevel'] = str(columndimension.outline_level) - if columndimension.collapsed: - col_def['collapsed'] = 'true' - if columndimension.auto_size: - col_def['bestFit'] = 'true' - if columndimension.width > 0: - col_def['width'] = str(columndimension.width) - else: - col_def['width'] = '9.10' - tag(doc, 'col', col_def) - end_tag(doc, 'cols') - - -def write_worksheet_data(doc, worksheet, string_table, style_table): - """Write worksheet data to xml.""" - start_tag(doc, 'sheetData') - max_column = worksheet.get_highest_column() - style_id_by_hash = style_table - cells_by_row = {} - for cell in worksheet.get_cell_collection(): - cells_by_row.setdefault(cell.row, []).append(cell) - for row_idx in sorted(cells_by_row): - row_dimension = worksheet.row_dimensions[row_idx] - attrs = {'r': '%d' % row_idx, - 'spans': '1:%d' % max_column} - if row_dimension.height > 0: - attrs['ht'] = str(row_dimension.height) - attrs['customHeight'] = '1' - start_tag(doc, 'row', attrs) - row_cells = cells_by_row[row_idx] - sorted_cells = sorted(row_cells, key = row_sort) - for cell in sorted_cells: - value = cell._value - coordinate = cell.get_coordinate() - attributes = {'r': coordinate} - attributes['t'] = cell.data_type - if coordinate in worksheet._styles: - attributes['s'] = '%d' % style_id_by_hash[ - hash(worksheet._styles[coordinate])] - start_tag(doc, 'c', attributes) - if value is None: - tag(doc, 'v', body='') - elif cell.data_type == cell.TYPE_STRING: - tag(doc, 'v', body = '%s' % string_table[value]) - elif cell.data_type == cell.TYPE_FORMULA: - tag(doc, 'f', body = '%s' % value[1:]) - tag(doc, 'v') - elif cell.data_type == cell.TYPE_NUMERIC: - tag(doc, 'v', body = '%s' % value) - else: - tag(doc, 'v', body = '%s' % value) - end_tag(doc, 'c') - end_tag(doc, 'row') - end_tag(doc, 'sheetData') - - -def write_worksheet_hyperlinks(doc, worksheet): - """Write worksheet hyperlinks to xml.""" - write_hyperlinks = False - for cell in worksheet.get_cell_collection(): - if cell.hyperlink_rel_id is not None: - write_hyperlinks = True - break - if write_hyperlinks: - start_tag(doc, 'hyperlinks') - for cell in worksheet.get_cell_collection(): - if cell.hyperlink_rel_id is not None: - attrs = {'display': cell.hyperlink, - 'ref': cell.get_coordinate(), - 'r:id': cell.hyperlink_rel_id} - tag(doc, 'hyperlink', attrs) - end_tag(doc, 'hyperlinks') - - -def write_worksheet_rels(worksheet, idx): - """Write relationships for the worksheet to xml.""" - root = Element('Relationships', {'xmlns': 'http://schemas.openxmlformats.org/package/2006/relationships'}) - for rel in worksheet.relationships: - attrs = {'Id': rel.id, 'Type': rel.type, 'Target': rel.target} - if rel.target_mode: - attrs['TargetMode'] = rel.target_mode - SubElement(root, 'Relationship', attrs) - if worksheet._charts: - attrs = {'Id' : 'rId1', - 'Type' : 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing', - 'Target' : '../drawings/drawing%s.xml' % idx } - SubElement(root, 'Relationship', attrs) - return get_document_content(root) diff --git a/tablib/packages/openpyxl3/__init__.py b/tablib/packages/openpyxl3/__init__.py deleted file mode 100644 index 81381d78..00000000 --- a/tablib/packages/openpyxl3/__init__.py +++ /dev/null @@ -1,53 +0,0 @@ -# file openpyxl/__init__.py - -# Copyright (c) 2010 openpyxl -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -# @license: http://www.opensource.org/licenses/mit-license.php -# @author: Eric Gazoni - -"""Imports for the openpyxl package.""" - -# package imports -from . import cell -from . import namedrange -from . import style -from . import workbook -from . import worksheet -from . import reader -from . import shared -from . import writer - -# constants - -__major__ = 1 # for major interface/format changes -__minor__ = 5 # for minor interface/format changes -__release__ = 2 # for tweaks, bug-fixes, or development - -__version__ = '%d.%d.%d' % (__major__, __minor__, __release__) - -__author__ = 'Eric Gazoni' -__license__ = 'MIT/Expat' -__author_email__ = 'eric.gazoni@gmail.com' -__maintainer_email__ = 'openpyxl-users@googlegroups.com' -__url__ = 'http://bitbucket.org/ericgazoni/openpyxl/wiki/Home' -__downloadUrl__ = "http://bitbucket.org/ericgazoni/openpyxl/downloads" - -__all__ = ('reader', 'shared', 'writer',) diff --git a/tablib/packages/openpyxl3/cell.py b/tablib/packages/openpyxl3/cell.py deleted file mode 100644 index 1171fdeb..00000000 --- a/tablib/packages/openpyxl3/cell.py +++ /dev/null @@ -1,384 +0,0 @@ -# file openpyxl/cell.py - -# Copyright (c) 2010 openpyxl -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -# @license: http://www.opensource.org/licenses/mit-license.php -# @author: Eric Gazoni - -"""Manage individual cells in a spreadsheet. - -The Cell class is required to know its value and type, display options, -and any other features of an Excel cell. Utilities for referencing -cells using Excel's 'A1' column/row nomenclature are also provided. - -""" - -__docformat__ = "restructuredtext en" - -# Python stdlib imports -import datetime -import re - -# package imports -from .shared.date_time import SharedDate -from .shared.exc import CellCoordinatesException, \ - ColumnStringIndexException, DataTypeException -from .style import NumberFormat - -# constants -COORD_RE = re.compile('^[$]?([A-Z]+)[$]?(\d+)$') - -ABSOLUTE_RE = re.compile('^[$]?([A-Z]+)[$]?(\d+)(:[$]?([A-Z]+)[$]?(\d+))?$') - -def coordinate_from_string(coord_string): - """Convert a coordinate string like 'B12' to a tuple ('B', 12)""" - match = COORD_RE.match(coord_string.upper()) - if not match: - msg = 'Invalid cell coordinates (%s)' % coord_string - raise CellCoordinatesException(msg) - column, row = match.groups() - return (column, int(row)) - - -def absolute_coordinate(coord_string): - """Convert a coordinate to an absolute coordinate string (B12 -> $B$12)""" - parts = ABSOLUTE_RE.match(coord_string).groups() - - if all(parts[-2:]): - return '$%s$%s:$%s$%s' % (parts[0], parts[1], parts[3], parts[4]) - else: - return '$%s$%s' % (parts[0], parts[1]) - - -def column_index_from_string(column, fast = False): - """Convert a column letter into a column number (e.g. B -> 2) - - Excel only supports 1-3 letter column names from A -> ZZZ, so we - restrict our column names to 1-3 characters, each in the range A-Z. - - .. note:: - - Fast mode is faster but does not check that all letters are capitals between A and Z - - """ - column = column.upper() - - clen = len(column) - - if not fast and not all('A' <= char <= 'Z' for char in column): - msg = 'Column string must contain only characters A-Z: got %s' % column - raise ColumnStringIndexException(msg) - - if clen == 1: - return ord(column[0]) - 64 - elif clen == 2: - return ((1 + (ord(column[0]) - 65)) * 26) + (ord(column[1]) - 64) - elif clen == 3: - return ((1 + (ord(column[0]) - 65)) * 676) + ((1 + (ord(column[1]) - 65)) * 26) + (ord(column[2]) - 64) - elif clen > 3: - raise ColumnStringIndexException('Column string index can not be longer than 3 characters') - else: - raise ColumnStringIndexException('Column string index can not be empty') - - -def get_column_letter(col_idx): - """Convert a column number into a column letter (3 -> 'C') - - Right shift the column col_idx by 26 to find column letters in reverse - order. These numbers are 1-based, and can be converted to ASCII - ordinals by adding 64. - - """ - # these indicies corrospond to A -> ZZZ and include all allowed - # columns - if not 1 <= col_idx <= 18278: - msg = 'Column index out of bounds: %s' % col_idx - raise ColumnStringIndexException(msg) - ordinals = [] - temp = col_idx - while temp: - quotient, remainder = divmod(temp, 26) - # check for exact division and borrow if needed - if remainder == 0: - quotient -= 1 - remainder = 26 - ordinals.append(remainder + 64) - temp = quotient - ordinals.reverse() - return ''.join([chr(ordinal) for ordinal in ordinals]) - - -class Cell(object): - """Describes cell associated properties. - - Properties of interest include style, type, value, and address. - - """ - __slots__ = ('column', - 'row', - '_value', - '_data_type', - 'parent', - 'xf_index', - '_hyperlink_rel') - - ERROR_CODES = {'#NULL!': 0, - '#DIV/0!': 1, - '#VALUE!': 2, - '#REF!': 3, - '#NAME?': 4, - '#NUM!': 5, - '#N/A': 6} - - TYPE_STRING = 's' - TYPE_FORMULA = 'f' - TYPE_NUMERIC = 'n' - TYPE_BOOL = 'b' - TYPE_NULL = 's' - TYPE_INLINE = 'inlineStr' - TYPE_ERROR = 'e' - - VALID_TYPES = [TYPE_STRING, TYPE_FORMULA, TYPE_NUMERIC, TYPE_BOOL, - TYPE_NULL, TYPE_INLINE, TYPE_ERROR] - - RE_PATTERNS = { - 'percentage': re.compile('^\-?[0-9]*\.?[0-9]*\s?\%$'), - 'time': re.compile('^(\d|[0-1]\d|2[0-3]):[0-5]\d(:[0-5]\d)?$'), - 'numeric': re.compile('^\-?([0-9]+\\.?[0-9]*|[0-9]*\\.?[0-9]+)((E|e)\-?[0-9]+)?$'), } - - def __init__(self, worksheet, column, row, value = None): - self.column = column.upper() - self.row = row - # _value is the stored value, while value is the displayed value - self._value = None - self._hyperlink_rel = None - self._data_type = self.TYPE_NULL - if value: - self.value = value - self.parent = worksheet - self.xf_index = 0 - - def __repr__(self): - return "" % (self.parent.title, self.get_coordinate()) - - def check_string(self, value): - """Check string coding, length, and line break character""" - # convert to unicode string - value = str(value) - # string must never be longer than 32,767 characters - # truncate if necessary - value = value[:32767] - # we require that newline is represented as "\n" in core, - # not as "\r\n" or "\r" - value = value.replace('\r\n', '\n') - return value - - def check_numeric(self, value): - """Cast value to int or float if necessary""" - if not isinstance(value, (int, float)): - try: - value = int(value) - except ValueError: - value = float(value) - return value - - def set_value_explicit(self, value = None, data_type = TYPE_STRING): - """Coerce values according to their explicit type""" - type_coercion_map = { - self.TYPE_INLINE: self.check_string, - self.TYPE_STRING: self.check_string, - self.TYPE_FORMULA: str, - self.TYPE_NUMERIC: self.check_numeric, - self.TYPE_BOOL: bool, } - try: - self._value = type_coercion_map[data_type](value) - except KeyError: - if data_type not in self.VALID_TYPES: - msg = 'Invalid data type: %s' % data_type - raise DataTypeException(msg) - self._data_type = data_type - - def data_type_for_value(self, value): - """Given a value, infer the correct data type""" - if value is None: - data_type = self.TYPE_NULL - elif value is True or value is False: - data_type = self.TYPE_BOOL - elif isinstance(value, (int, float)): - data_type = self.TYPE_NUMERIC - elif not value: - data_type = self.TYPE_STRING - elif isinstance(value, (datetime.datetime, datetime.date)): - data_type = self.TYPE_NUMERIC - elif isinstance(value, str) and value[0] == '=': - data_type = self.TYPE_FORMULA - elif self.RE_PATTERNS['numeric'].match(value): - data_type = self.TYPE_NUMERIC - elif value.strip() in self.ERROR_CODES: - data_type = self.TYPE_ERROR - else: - data_type = self.TYPE_STRING - return data_type - - def bind_value(self, value): - """Given a value, infer type and display options.""" - self._data_type = self.data_type_for_value(value) - if value is None: - self.set_value_explicit('', self.TYPE_NULL) - return True - elif self._data_type == self.TYPE_STRING: - # percentage detection - percentage_search = self.RE_PATTERNS['percentage'].match(value) - if percentage_search and value.strip() != '%': - value = float(value.replace('%', '')) / 100.0 - self.set_value_explicit(value, self.TYPE_NUMERIC) - self._set_number_format(NumberFormat.FORMAT_PERCENTAGE) - return True - # time detection - time_search = self.RE_PATTERNS['time'].match(value) - if time_search: - sep_count = value.count(':') #pylint: disable-msg=E1103 - if sep_count == 1: - hours, minutes = [int(bit) for bit in value.split(':')] #pylint: disable-msg=E1103 - seconds = 0 - elif sep_count == 2: - hours, minutes, seconds = \ - [int(bit) for bit in value.split(':')] #pylint: disable-msg=E1103 - days = (hours / 24.0) + (minutes / 1440.0) + \ - (seconds / 86400.0) - self.set_value_explicit(days, self.TYPE_NUMERIC) - self._set_number_format(NumberFormat.FORMAT_DATE_TIME3) - return True - if self._data_type == self.TYPE_NUMERIC: - # date detection - # if the value is a date, but not a date time, make it a - # datetime, and set the time part to 0 - if isinstance(value, datetime.date) and not \ - isinstance(value, datetime.datetime): - value = datetime.datetime.combine(value, datetime.time()) - if isinstance(value, datetime.datetime): - value = SharedDate().datetime_to_julian(date = value) - self.set_value_explicit(value, self.TYPE_NUMERIC) - self._set_number_format(NumberFormat.FORMAT_DATE_YYYYMMDD2) - return True - self.set_value_explicit(value, self._data_type) - - def _get_value(self): - """Return the value, formatted as a date if needed""" - value = self._value - if self.is_date(): - value = SharedDate().from_julian(value) - return value - - def _set_value(self, value): - """Set the value and infer type and display options.""" - self.bind_value(value) - - value = property(_get_value, _set_value, - doc = 'Get or set the value held in the cell.\n\n' - ':rtype: depends on the value (string, float, int or ' - ':class:`datetime.datetime`)') - - def _set_hyperlink(self, val): - """Set value and display for hyperlinks in a cell""" - if self._hyperlink_rel is None: - self._hyperlink_rel = self.parent.create_relationship("hyperlink") - self._hyperlink_rel.target = val - self._hyperlink_rel.target_mode = "External" - if self._value is None: - self.value = val - - def _get_hyperlink(self): - """Return the hyperlink target or an empty string""" - return self._hyperlink_rel is not None and \ - self._hyperlink_rel.target or '' - - hyperlink = property(_get_hyperlink, _set_hyperlink, - doc = 'Get or set the hyperlink held in the cell. ' - 'Automatically sets the `value` of the cell with link text, ' - 'but you can modify it afterwards by setting the ' - '`value` property, and the hyperlink will remain.\n\n' - ':rtype: string') - - @property - def hyperlink_rel_id(self): - """Return the id pointed to by the hyperlink, or None""" - return self._hyperlink_rel is not None and \ - self._hyperlink_rel.id or None - - def _set_number_format(self, format_code): - """Set a new formatting code for numeric values""" - self.style.number_format.format_code = format_code - - @property - def has_style(self): - """Check if the parent worksheet has a style for this cell""" - return self.get_coordinate() in self.parent._styles #pylint: disable-msg=W0212 - - @property - def style(self): - """Returns the :class:`.style.Style` object for this cell""" - return self.parent.get_style(self.get_coordinate()) - - @property - def data_type(self): - """Return the data type represented by this cell""" - return self._data_type - - def get_coordinate(self): - """Return the coordinate string for this cell (e.g. 'B12') - - :rtype: string - """ - return '%s%s' % (self.column, self.row) - - @property - def address(self): - """Return the coordinate string for this cell (e.g. 'B12') - - :rtype: string - """ - return self.get_coordinate() - - def offset(self, row = 0, column = 0): - """Returns a cell location relative to this cell. - - :param row: number of rows to offset - :type row: int - - :param column: number of columns to offset - :type column: int - - :rtype: :class:`.cell.Cell` - """ - offset_column = get_column_letter(column_index_from_string( - column = self.column) + column) - offset_row = self.row + row - return self.parent.cell('%s%s' % (offset_column, offset_row)) - - def is_date(self): - """Returns whether the value is *probably* a date or not - - :rtype: bool - """ - return (self.has_style - and self.style.number_format.is_date_format() - and isinstance(self._value, (int, float))) diff --git a/tablib/packages/openpyxl3/chart.py b/tablib/packages/openpyxl3/chart.py deleted file mode 100644 index 265ccafa..00000000 --- a/tablib/packages/openpyxl3/chart.py +++ /dev/null @@ -1,340 +0,0 @@ -''' -Copyright (c) 2010 openpyxl - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - -@license: http://www.opensource.org/licenses/mit-license.php -@author: Eric Gazoni -''' - -import math - -from .style import NumberFormat -from .drawing import Drawing, Shape -from .shared.units import pixels_to_EMU, short_color -from .cell import get_column_letter - -class Axis(object): - - POSITION_BOTTOM = 'b' - POSITION_LEFT = 'l' - - ORIENTATION_MIN_MAX = "minMax" - - def __init__(self): - - self.orientation = self.ORIENTATION_MIN_MAX - self.number_format = NumberFormat() - for attr in ('position','tick_label_position','crosses', - 'auto','label_align','label_offset','cross_between'): - setattr(self, attr, None) - self.min = 0 - self.max = None - self.unit = None - - @classmethod - def default_category(cls): - """ default values for category axes """ - - ax = Axis() - ax.id = 60871424 - ax.cross = 60873344 - ax.position = Axis.POSITION_BOTTOM - ax.tick_label_position = 'nextTo' - ax.crosses = "autoZero" - ax.auto = True - ax.label_align = 'ctr' - ax.label_offset = 100 - return ax - - @classmethod - def default_value(cls): - """ default values for value axes """ - - ax = Axis() - ax.id = 60873344 - ax.cross = 60871424 - ax.position = Axis.POSITION_LEFT - ax.major_gridlines = None - ax.tick_label_position = 'nextTo' - ax.crosses = 'autoZero' - ax.auto = False - ax.cross_between = 'between' - return ax - -class Reference(object): - """ a simple wrapper around a serie of reference data """ - - def __init__(self, sheet, pos1, pos2=None): - - self.sheet = sheet - self.pos1 = pos1 - self.pos2 = pos2 - - def get_type(self): - - if isinstance(self.cache[0], str): - return 'str' - else: - return 'num' - - def _get_ref(self): - """ format excel reference notation """ - - if self.pos2: - return '%s!$%s$%s:$%s$%s' % (self.sheet.title, - get_column_letter(self.pos1[1]+1), self.pos1[0]+1, - get_column_letter(self.pos2[1]+1), self.pos2[0]+1) - else: - return '%s!$%s$%s' % (self.sheet.title, - get_column_letter(self.pos1[1]+1), self.pos1[0]+1) - - - def _get_cache(self): - """ read data in sheet - to be used at writing time """ - - cache = [] - if self.pos2: - for row in range(self.pos1[0], self.pos2[0]+1): - for col in range(self.pos1[1], self.pos2[1]+1): - cache.append(self.sheet.cell(row=row, column=col).value) - else: - cell = self.sheet.cell(row=self.pos1[0], column=self.pos1[1]) - cache.append(cell.value) - return cache - - -class Serie(object): - """ a serie of data and possibly associated labels """ - - MARKER_NONE = 'none' - - def __init__(self, values, labels=None, legend=None, color=None, xvalues=None): - - self.marker = Serie.MARKER_NONE - self.values = values - self.xvalues = xvalues - self.labels = labels - self.legend = legend - self.error_bar = None - self._color = color - - def _get_color(self): - return self._color - - def _set_color(self, color): - self._color = short_color(color) - - color = property(_get_color, _set_color) - - def get_min_max(self): - - if self.error_bar: - err_cache = self.error_bar.values._get_cache() - vals = [v + err_cache[i] \ - for i,v in enumerate(self.values._get_cache())] - else: - vals = self.values._get_cache() - return min(vals), max(vals) - - def __len__(self): - - return len(self.values.cache) - -class Legend(object): - - def __init__(self): - - self.position = 'r' - self.layout = None - -class ErrorBar(object): - - PLUS = 1 - MINUS = 2 - PLUS_MINUS = 3 - - def __init__(self, _type, values): - - self.type = _type - self.values = values - -class Chart(object): - """ raw chart class """ - - GROUPING_CLUSTERED = 'clustered' - GROUPING_STANDARD = 'standard' - - BAR_CHART = 1 - LINE_CHART = 2 - SCATTER_CHART = 3 - - def __init__(self, _type, grouping): - - self._series = [] - - # public api - self.type = _type - self.grouping = grouping - self.x_axis = Axis.default_category() - self.y_axis = Axis.default_value() - self.legend = Legend() - self.lang = 'fr-FR' - self.title = '' - self.print_margins = dict(b=.75, l=.7, r=.7, t=.75, header=0.3, footer=.3) - - # the containing drawing - self.drawing = Drawing() - - # the offset for the plot part in percentage of the drawing size - self.width = .6 - self.height = .6 - self.margin_top = self._get_max_margin_top() - self.margin_left = 0 - - # the user defined shapes - self._shapes = [] - - def add_serie(self, serie): - - serie.id = len(self._series) - self._series.append(serie) - self._compute_min_max() - if not None in [s.xvalues for s in self._series]: - self._compute_xmin_xmax() - - def add_shape(self, shape): - - shape._chart = self - self._shapes.append(shape) - - def get_x_units(self): - """ calculate one unit for x axis in EMU """ - - return max([len(s.values._get_cache()) for s in self._series]) - - def get_y_units(self): - """ calculate one unit for y axis in EMU """ - - dh = pixels_to_EMU(self.drawing.height) - return (dh * self.height) / self.y_axis.max - - def get_y_chars(self): - """ estimate nb of chars for y axis """ - - _max = max([max(s.values._get_cache()) for s in self._series]) - return len(str(int(_max))) - - def _compute_min_max(self): - """ compute y axis limits and units """ - - maxi = max([max(s.values._get_cache()) for s in self._series]) - - mul = None - if maxi < 1: - s = str(maxi).split('.')[1] - mul = 10 - for x in s: - if x == '0': - mul *= 10 - else: - break - maxi = maxi * mul - - maxi = math.ceil(maxi * 1.1) - sz = len(str(int(maxi))) - 1 - unit = math.ceil(math.ceil(maxi / pow(10, sz)) * pow(10, sz-1)) - maxi = math.ceil(maxi/unit) * unit - - if mul is not None: - maxi = maxi/mul - unit = unit/mul - - if maxi / unit > 9: - # no more that 10 ticks - unit *= 2 - - self.y_axis.max = maxi - self.y_axis.unit = unit - - def _compute_xmin_xmax(self): - """ compute x axis limits and units """ - - maxi = max([max(s.xvalues._get_cache()) for s in self._series]) - - mul = None - if maxi < 1: - s = str(maxi).split('.')[1] - mul = 10 - for x in s: - if x == '0': - mul *= 10 - else: - break - maxi = maxi * mul - - maxi = math.ceil(maxi * 1.1) - sz = len(str(int(maxi))) - 1 - unit = math.ceil(math.ceil(maxi / pow(10, sz)) * pow(10, sz-1)) - maxi = math.ceil(maxi/unit) * unit - - if mul is not None: - maxi = maxi/mul - unit = unit/mul - - if maxi / unit > 9: - # no more that 10 ticks - unit *= 2 - - self.x_axis.max = maxi - self.x_axis.unit = unit - - def _get_max_margin_top(self): - - mb = Shape.FONT_HEIGHT + Shape.MARGIN_BOTTOM - plot_height = self.drawing.height * self.height - return float(self.drawing.height - plot_height - mb)/self.drawing.height - - def _get_min_margin_left(self): - - ml = (self.get_y_chars() * Shape.FONT_WIDTH) + Shape.MARGIN_LEFT - return float(ml)/self.drawing.width - - def _get_margin_top(self): - """ get margin in percent """ - - return min(self.margin_top, self._get_max_margin_top()) - - def _get_margin_left(self): - - return max(self._get_min_margin_left(), self.margin_left) - -class BarChart(Chart): - def __init__(self): - super(BarChart, self).__init__(Chart.BAR_CHART, Chart.GROUPING_CLUSTERED) - -class LineChart(Chart): - def __init__(self): - super(LineChart, self).__init__(Chart.LINE_CHART, Chart.GROUPING_STANDARD) - -class ScatterChart(Chart): - def __init__(self): - super(ScatterChart, self).__init__(Chart.SCATTER_CHART, Chart.GROUPING_STANDARD) - - diff --git a/tablib/packages/openpyxl3/drawing.py b/tablib/packages/openpyxl3/drawing.py deleted file mode 100644 index 00075697..00000000 --- a/tablib/packages/openpyxl3/drawing.py +++ /dev/null @@ -1,402 +0,0 @@ -''' -Copyright (c) 2010 openpyxl - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - -@license: http://www.opensource.org/licenses/mit-license.php -@author: Eric Gazoni -''' - -import math -from .style import Color -from .shared.units import pixels_to_EMU, EMU_to_pixels, short_color - -class Shadow(object): - - SHADOW_BOTTOM = 'b' - SHADOW_BOTTOM_LEFT = 'bl' - SHADOW_BOTTOM_RIGHT = 'br' - SHADOW_CENTER = 'ctr' - SHADOW_LEFT = 'l' - SHADOW_TOP = 't' - SHADOW_TOP_LEFT = 'tl' - SHADOW_TOP_RIGHT = 'tr' - - def __init__(self): - self.visible = False - self.blurRadius = 6 - self.distance = 2 - self.direction = 0 - self.alignment = self.SHADOW_BOTTOM_RIGHT - self.color = Color(Color.BLACK) - self.alpha = 50 - -class Drawing(object): - """ a drawing object - eg container for shapes or charts - we assume user specifies dimensions in pixels; units are - converted to EMU in the drawing part - """ - - count = 0 - - def __init__(self): - - self.name = '' - self.description = '' - self.coordinates = ((1,2), (16,8)) - self.left = 0 - self.top = 0 - self._width = EMU_to_pixels(200000) - self._height = EMU_to_pixels(1828800) - self.resize_proportional = False - self.rotation = 0 -# self.shadow = Shadow() - - def _set_width(self, w): - - if self.resize_proportional and w: - ratio = self._height / self._width - self._height = round(ratio * w) - self._width = w - - def _get_width(self): - - return self._width - - width = property(_get_width, _set_width) - - def _set_height(self, h): - - if self.resize_proportional and h: - ratio = self._width / self._height - self._width = round(ratio * h) - self._height = h - - def _get_height(self): - - return self._height - - height = property(_get_height, _set_height) - - def set_dimension(self, w=0, h=0): - - xratio = w / self._width - yratio = h / self._height - - if self.resize_proportional and w and h: - if (xratio * self._height) < h: - self._height = math.ceil(xratio * self._height) - self._width = width - else: - self._width = math.ceil(yratio * self._width) - self._height = height - - def get_emu_dimensions(self): - """ return (x, y, w, h) in EMU """ - - return (pixels_to_EMU(self.left), pixels_to_EMU(self.top), - pixels_to_EMU(self._width), pixels_to_EMU(self._height)) - - -class Shape(object): - """ a drawing inside a chart - coordiantes are specified by the user in the axis units - """ - - MARGIN_LEFT = 6 + 13 + 1 - MARGIN_BOTTOM = 17 + 11 - - FONT_WIDTH = 7 - FONT_HEIGHT = 8 - - ROUND_RECT = 'roundRect' - RECT = 'rect' - - # other shapes to define : - ''' - "line" - "lineInv" - "triangle" - "rtTriangle" - "diamond" - "parallelogram" - "trapezoid" - "nonIsoscelesTrapezoid" - "pentagon" - "hexagon" - "heptagon" - "octagon" - "decagon" - "dodecagon" - "star4" - "star5" - "star6" - "star7" - "star8" - "star10" - "star12" - "star16" - "star24" - "star32" - "roundRect" - "round1Rect" - "round2SameRect" - "round2DiagRect" - "snipRoundRect" - "snip1Rect" - "snip2SameRect" - "snip2DiagRect" - "plaque" - "ellipse" - "teardrop" - "homePlate" - "chevron" - "pieWedge" - "pie" - "blockArc" - "donut" - "noSmoking" - "rightArrow" - "leftArrow" - "upArrow" - "downArrow" - "stripedRightArrow" - "notchedRightArrow" - "bentUpArrow" - "leftRightArrow" - "upDownArrow" - "leftUpArrow" - "leftRightUpArrow" - "quadArrow" - "leftArrowCallout" - "rightArrowCallout" - "upArrowCallout" - "downArrowCallout" - "leftRightArrowCallout" - "upDownArrowCallout" - "quadArrowCallout" - "bentArrow" - "uturnArrow" - "circularArrow" - "leftCircularArrow" - "leftRightCircularArrow" - "curvedRightArrow" - "curvedLeftArrow" - "curvedUpArrow" - "curvedDownArrow" - "swooshArrow" - "cube" - "can" - "lightningBolt" - "heart" - "sun" - "moon" - "smileyFace" - "irregularSeal1" - "irregularSeal2" - "foldedCorner" - "bevel" - "frame" - "halfFrame" - "corner" - "diagStripe" - "chord" - "arc" - "leftBracket" - "rightBracket" - "leftBrace" - "rightBrace" - "bracketPair" - "bracePair" - "straightConnector1" - "bentConnector2" - "bentConnector3" - "bentConnector4" - "bentConnector5" - "curvedConnector2" - "curvedConnector3" - "curvedConnector4" - "curvedConnector5" - "callout1" - "callout2" - "callout3" - "accentCallout1" - "accentCallout2" - "accentCallout3" - "borderCallout1" - "borderCallout2" - "borderCallout3" - "accentBorderCallout1" - "accentBorderCallout2" - "accentBorderCallout3" - "wedgeRectCallout" - "wedgeRoundRectCallout" - "wedgeEllipseCallout" - "cloudCallout" - "cloud" - "ribbon" - "ribbon2" - "ellipseRibbon" - "ellipseRibbon2" - "leftRightRibbon" - "verticalScroll" - "horizontalScroll" - "wave" - "doubleWave" - "plus" - "flowChartProcess" - "flowChartDecision" - "flowChartInputOutput" - "flowChartPredefinedProcess" - "flowChartInternalStorage" - "flowChartDocument" - "flowChartMultidocument" - "flowChartTerminator" - "flowChartPreparation" - "flowChartManualInput" - "flowChartManualOperation" - "flowChartConnector" - "flowChartPunchedCard" - "flowChartPunchedTape" - "flowChartSummingJunction" - "flowChartOr" - "flowChartCollate" - "flowChartSort" - "flowChartExtract" - "flowChartMerge" - "flowChartOfflineStorage" - "flowChartOnlineStorage" - "flowChartMagneticTape" - "flowChartMagneticDisk" - "flowChartMagneticDrum" - "flowChartDisplay" - "flowChartDelay" - "flowChartAlternateProcess" - "flowChartOffpageConnector" - "actionButtonBlank" - "actionButtonHome" - "actionButtonHelp" - "actionButtonInformation" - "actionButtonForwardNext" - "actionButtonBackPrevious" - "actionButtonEnd" - "actionButtonBeginning" - "actionButtonReturn" - "actionButtonDocument" - "actionButtonSound" - "actionButtonMovie" - "gear6" - "gear9" - "funnel" - "mathPlus" - "mathMinus" - "mathMultiply" - "mathDivide" - "mathEqual" - "mathNotEqual" - "cornerTabs" - "squareTabs" - "plaqueTabs" - "chartX" - "chartStar" - "chartPlus" - ''' - - def __init__(self, coordinates=((0,0), (1,1)), text=None, scheme="accent1"): - - self.coordinates = coordinates # in axis unit - self.text = text - self.scheme = scheme - self.style = Shape.RECT - self._border_width = 3175 # in EMU - self._border_color = Color.BLACK[2:] #"F3B3C5" - self._color = Color.WHITE[2:] - self._text_color = Color.BLACK[2:] - - def _get_border_color(self): - return self._border_color - - def _set_border_color(self, color): - self._border_color = short_color(color) - - border_color = property(_get_border_color, _set_border_color) - - def _get_color(self): - return self._color - - def _set_color(self, color): - self._color = short_color(color) - - color = property(_get_color, _set_color) - - def _get_text_color(self): - return self._text_color - - def _set_text_color(self, color): - self._text_color = short_color(color) - - text_color = property(_get_text_color, _set_text_color) - - def _get_border_width(self): - - return EMU_to_pixels(self._border_width) - - def _set_border_width(self, w): - - self._border_width = pixels_to_EMU(w) - print(self._border_width) - - border_width = property(_get_border_width, _set_border_width) - - def get_coordinates(self): - """ return shape coordinates in percentages (left, top, right, bottom) """ - - (x1, y1), (x2, y2) = self.coordinates - - drawing_width = pixels_to_EMU(self._chart.drawing.width) - drawing_height = pixels_to_EMU(self._chart.drawing.height) - plot_width = drawing_width * self._chart.width - plot_height = drawing_height * self._chart.height - - margin_left = self._chart._get_margin_left() * drawing_width - xunit = plot_width / self._chart.get_x_units() - - margin_top = self._chart._get_margin_top() * drawing_height - yunit = self._chart.get_y_units() - - x_start = (margin_left + (float(x1) * xunit)) / drawing_width - y_start = (margin_top + plot_height - (float(y1) * yunit)) / drawing_height - - x_end = (margin_left + (float(x2) * xunit)) / drawing_width - y_end = (margin_top + plot_height - (float(y2) * yunit)) / drawing_height - - def _norm_pct(pct): - """ force shapes to appear by truncating too large sizes """ - if pct>1: pct = 1 - elif pct<0: pct = 0 - return pct - - # allow user to specify y's in whatever order - # excel expect y_end to be lower - if y_end < y_start: - y_end, y_start = y_start, y_end - - return (_norm_pct(x_start), _norm_pct(y_start), - _norm_pct(x_end), _norm_pct(y_end)) - \ No newline at end of file diff --git a/tablib/packages/openpyxl3/namedrange.py b/tablib/packages/openpyxl3/namedrange.py deleted file mode 100644 index 85b08a88..00000000 --- a/tablib/packages/openpyxl3/namedrange.py +++ /dev/null @@ -1,68 +0,0 @@ -# file openpyxl/namedrange.py - -# Copyright (c) 2010 openpyxl -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -# @license: http://www.opensource.org/licenses/mit-license.php -# @author: Eric Gazoni - -"""Track named groups of cells in a worksheet""" - -# Python stdlib imports -import re - -# package imports -from .shared.exc import NamedRangeException - -# constants -NAMED_RANGE_RE = re.compile("'?([^']*)'?!((\$([A-Za-z]+))?\$([0-9]+)(:(\$([A-Za-z]+))?(\$([0-9]+)))?)$") - -class NamedRange(object): - """A named group of cells""" - __slots__ = ('name', 'destinations', 'local_only') - - def __init__(self, name, destinations): - self.name = name - self.destinations = destinations - self.local_only = False - - def __str__(self): - return ','.join(['%s!%s' % (sheet, name) for sheet, name in self.destinations]) - - def __repr__(self): - - return '<%s "%s">' % (self.__class__.__name__, str(self)) - - -def split_named_range(range_string): - """Separate a named range into its component parts""" - - destinations = [] - - for range_string in range_string.split(','): - - match = NAMED_RANGE_RE.match(range_string) - if not match: - raise NamedRangeException('Invalid named range string: "%s"' % range_string) - else: - sheet_name, xlrange = match.groups()[:2] - destinations.append((sheet_name, xlrange)) - - return destinations diff --git a/tablib/packages/openpyxl3/reader/__init__.py b/tablib/packages/openpyxl3/reader/__init__.py deleted file mode 100644 index 76f10f8d..00000000 --- a/tablib/packages/openpyxl3/reader/__init__.py +++ /dev/null @@ -1,33 +0,0 @@ -# file openpyxl/reader/__init__.py - -# Copyright (c) 2010 openpyxl -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -# @license: http://www.opensource.org/licenses/mit-license.php -# @author: Eric Gazoni - -"""Imports for the openpyxl.reader namespace.""" - -# package imports -from . import excel -from . import strings -from . import style -from . import workbook -from . import worksheet diff --git a/tablib/packages/openpyxl3/reader/excel.py b/tablib/packages/openpyxl3/reader/excel.py deleted file mode 100644 index 1052af5a..00000000 --- a/tablib/packages/openpyxl3/reader/excel.py +++ /dev/null @@ -1,121 +0,0 @@ -# file openpyxl/reader/excel.py - -# Copyright (c) 2010 openpyxl -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -# @license: http://www.opensource.org/licenses/mit-license.php -# @author: Eric Gazoni - -"""Read an xlsx file into Python""" - -# Python stdlib imports -from zipfile import ZipFile, ZIP_DEFLATED, BadZipfile - -# package imports -from ..shared.exc import OpenModeError, InvalidFileException -from ..shared.ooxml import ARC_SHARED_STRINGS, ARC_CORE, ARC_APP, \ - ARC_WORKBOOK, PACKAGE_WORKSHEETS, ARC_STYLE -from ..workbook import Workbook -from .strings import read_string_table -from .style import read_style_table -from .workbook import read_sheets_titles, read_named_ranges, \ - read_properties_core, get_sheet_ids -from .worksheet import read_worksheet -from .iter_worksheet import unpack_worksheet - -def load_workbook(filename, use_iterators = False): - """Open the given filename and return the workbook - - :param filename: the path to open - :type filename: string - - :param use_iterators: use lazy load for cells - :type use_iterators: bool - - :rtype: :class:`..workbook.Workbook` - - .. note:: - - When using lazy load, all worksheets will be :class:`.iter_worksheet.IterableWorksheet` - and the returned workbook will be read-only. - - """ - - try: - # fileobject must have been opened with 'rb' flag - # it is required by zipfile - if 'b' not in filename.mode: - raise OpenModeError("File-object must be opened in binary mode") - except AttributeError: - # filename is not an object - # it doesn't have mode attribute - pass - - try: - archive = ZipFile(filename, 'r', ZIP_DEFLATED) - except (BadZipfile, RuntimeError, IOError, ValueError) as e: - raise InvalidFileException(str(e)) - wb = Workbook() - - if use_iterators: - wb._set_optimized_read() - - try: - _load_workbook(wb, archive, filename, use_iterators) - except KeyError as e: - raise InvalidFileException(str(e)) - except Exception as e: - raise e - finally: - archive.close() - return wb - -def _load_workbook(wb, archive, filename, use_iterators): - - valid_files = archive.namelist() - - # get workbook-level information - wb.properties = read_properties_core(archive.read(ARC_CORE)) - try: - string_table = read_string_table(archive.read(ARC_SHARED_STRINGS)) - except KeyError: - string_table = {} - style_table = read_style_table(archive.read(ARC_STYLE)) - - # get worksheets - wb.worksheets = [] # remove preset worksheet - sheet_names = read_sheets_titles(archive.read(ARC_APP)) - for i, sheet_name in enumerate(sheet_names): - - sheet_codename = 'sheet%d.xml' % (i + 1) - worksheet_path = '%s/%s' % (PACKAGE_WORKSHEETS, sheet_codename) - - if not worksheet_path in valid_files: - continue - - if not use_iterators: - new_ws = read_worksheet(archive.read(worksheet_path), wb, sheet_name, string_table, style_table) - else: - xml_source = unpack_worksheet(archive, worksheet_path) - new_ws = read_worksheet(xml_source, wb, sheet_name, string_table, style_table, filename, sheet_codename) - #new_ws = read_worksheet(archive.read(worksheet_path), wb, sheet_name, string_table, style_table, filename, sheet_codename) - wb.add_sheet(new_ws, index = i) - - wb._named_ranges = read_named_ranges(archive.read(ARC_WORKBOOK), wb) diff --git a/tablib/packages/openpyxl3/reader/iter_worksheet.py b/tablib/packages/openpyxl3/reader/iter_worksheet.py deleted file mode 100644 index 670e6b1f..00000000 --- a/tablib/packages/openpyxl3/reader/iter_worksheet.py +++ /dev/null @@ -1,343 +0,0 @@ -# file openpyxl/reader/iter_worksheet.py - -# Copyright (c) 2010 openpyxl -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -# @license: http://www.opensource.org/licenses/mit-license.php -# @author: Eric Gazoni - -""" Iterators-based worksheet reader -*Still very raw* -""" - -from io import StringIO -import warnings -import operator -from functools import partial -from itertools import groupby -from ..worksheet import Worksheet -from ..cell import coordinate_from_string, get_column_letter, Cell -from .excel import get_sheet_ids -from .strings import read_string_table -from .style import read_style_table, NumberFormat -from ..shared.date_time import SharedDate -from .worksheet import read_dimension -from ..shared.ooxml import (MIN_COLUMN, MAX_COLUMN, PACKAGE_WORKSHEETS, - MAX_ROW, MIN_ROW, ARC_SHARED_STRINGS, ARC_APP, ARC_STYLE) -from xml.etree.cElementTree import iterparse -from zipfile import ZipFile -from .. import cell -import re -import tempfile -import zlib -import zipfile -import struct - -TYPE_NULL = Cell.TYPE_NULL -MISSING_VALUE = None - -RE_COORDINATE = re.compile('^([A-Z]+)([0-9]+)$') - -SHARED_DATE = SharedDate() - -_COL_CONVERSION_CACHE = dict((get_column_letter(i), i) for i in range(1, 18279)) -def column_index_from_string(str_col, _col_conversion_cache=_COL_CONVERSION_CACHE): - # we use a function argument to get indexed name lookup - return _col_conversion_cache[str_col] -del _COL_CONVERSION_CACHE - -RAW_ATTRIBUTES = ['row', 'column', 'coordinate', 'internal_value', 'data_type', 'style_id', 'number_format'] - -try: - from collections import namedtuple - BaseRawCell = namedtuple('RawCell', RAW_ATTRIBUTES) -except ImportError: - - warnings.warn("""Unable to import 'namedtuple' module, this may cause memory issues when using optimized reader. Please upgrade your Python installation to 2.6+""") - - class BaseRawCell(object): - - def __init__(self, *args): - assert len(args)==len(RAW_ATTRIBUTES) - - for attr, val in zip(RAW_ATTRIBUTES, args): - setattr(self, attr, val) - - def _replace(self, **kwargs): - - self.__dict__.update(kwargs) - - return self - - -class RawCell(BaseRawCell): - """Optimized version of the :class:`..cell.Cell`, using named tuples. - - Useful attributes are: - - * row - * column - * coordinate - * internal_value - - You can also access if needed: - - * data_type - * number_format - - """ - - @property - def is_date(self): - res = (self.data_type == Cell.TYPE_NUMERIC - and self.number_format is not None - and ('d' in self.number_format - or 'm' in self.number_format - or 'y' in self.number_format - or 'h' in self.number_format - or 's' in self.number_format - )) - - return res - -def iter_rows(workbook_name, sheet_name, xml_source, range_string = '', row_offset = 0, column_offset = 0): - - archive = get_archive_file(workbook_name) - - source = xml_source - - if range_string: - min_col, min_row, max_col, max_row = get_range_boundaries(range_string, row_offset, column_offset) - else: - min_col, min_row, max_col, max_row = read_dimension(xml_source = source) - min_col = column_index_from_string(min_col) - max_col = column_index_from_string(max_col) + 1 - max_row += 6 - - try: - string_table = read_string_table(archive.read(ARC_SHARED_STRINGS)) - except KeyError: - string_table = {} - - style_table = read_style_table(archive.read(ARC_STYLE)) - - source.seek(0) - p = iterparse(source) - - return get_squared_range(p, min_col, min_row, max_col, max_row, string_table, style_table) - - -def get_rows(p, min_column = MIN_COLUMN, min_row = MIN_ROW, max_column = MAX_COLUMN, max_row = MAX_ROW): - - return groupby(get_cells(p, min_row, min_column, max_row, max_column), operator.attrgetter('row')) - -def get_cells(p, min_row, min_col, max_row, max_col, _re_coordinate=RE_COORDINATE): - - for _event, element in p: - - if element.tag == '{http://schemas.openxmlformats.org/spreadsheetml/2006/main}c': - coord = element.get('r') - column_str, row = _re_coordinate.match(coord).groups() - - row = int(row) - column = column_index_from_string(column_str) - - if min_col <= column <= max_col and min_row <= row <= max_row: - data_type = element.get('t', 'n') - style_id = element.get('s') - value = element.findtext('{http://schemas.openxmlformats.org/spreadsheetml/2006/main}v') - yield RawCell(row, column_str, coord, value, data_type, style_id, None) - - if element.tag == '{http://schemas.openxmlformats.org/spreadsheetml/2006/main}v': - continue - element.clear() - - - -def get_range_boundaries(range_string, row = 0, column = 0): - - if ':' in range_string: - min_range, max_range = range_string.split(':') - min_col, min_row = coordinate_from_string(min_range) - max_col, max_row = coordinate_from_string(max_range) - - min_col = column_index_from_string(min_col) + column - max_col = column_index_from_string(max_col) + column - min_row += row - max_row += row - - else: - min_col, min_row = coordinate_from_string(range_string) - min_col = column_index_from_string(min_col) - max_col = min_col + 1 - max_row = min_row - - return (min_col, min_row, max_col, max_row) - -def get_archive_file(archive_name): - - return ZipFile(archive_name, 'r') - -def get_xml_source(archive_file, sheet_name): - - return archive_file.read('%s/%s' % (PACKAGE_WORKSHEETS, sheet_name)) - -def get_missing_cells(row, columns): - - return dict([(column, RawCell(row, column, '%s%s' % (column, row), MISSING_VALUE, TYPE_NULL, None, None)) for column in columns]) - -def get_squared_range(p, min_col, min_row, max_col, max_row, string_table, style_table): - - expected_columns = [get_column_letter(ci) for ci in range(min_col, max_col)] - - current_row = min_row - for row, cells in get_rows(p, min_row = min_row, max_row = max_row, min_column = min_col, max_column = max_col): - full_row = [] - if current_row < row: - - for gap_row in range(current_row, row): - - dummy_cells = get_missing_cells(gap_row, expected_columns) - - yield tuple([dummy_cells[column] for column in expected_columns]) - - current_row = row - - temp_cells = list(cells) - - retrieved_columns = dict([(c.column, c) for c in temp_cells]) - - missing_columns = list(set(expected_columns) - set(retrieved_columns.keys())) - - replacement_columns = get_missing_cells(row, missing_columns) - - for column in expected_columns: - - if column in retrieved_columns: - cell = retrieved_columns[column] - - if cell.style_id is not None: - style = style_table[int(cell.style_id)] - cell = cell._replace(number_format = style.number_format.format_code) #pylint: disable-msg=W0212 - if cell.internal_value is not None: - if cell.data_type == Cell.TYPE_STRING: - cell = cell._replace(internal_value = string_table[int(cell.internal_value)]) #pylint: disable-msg=W0212 - elif cell.data_type == Cell.TYPE_BOOL: - cell = cell._replace(internal_value = cell.internal_value == 'True') - elif cell.is_date: - cell = cell._replace(internal_value = SHARED_DATE.from_julian(float(cell.internal_value))) - elif cell.data_type == Cell.TYPE_NUMERIC: - cell = cell._replace(internal_value = float(cell.internal_value)) - full_row.append(cell) - - else: - full_row.append(replacement_columns[column]) - - current_row = row + 1 - - yield tuple(full_row) - -#------------------------------------------------------------------------------ - -class IterableWorksheet(Worksheet): - - def __init__(self, parent_workbook, title, workbook_name, - sheet_codename, xml_source): - - Worksheet.__init__(self, parent_workbook, title) - self._workbook_name = workbook_name - self._sheet_codename = sheet_codename - self._xml_source = xml_source - - def iter_rows(self, range_string = '', row_offset = 0, column_offset = 0): - """ Returns a squared range based on the `range_string` parameter, - using generators. - - :param range_string: range of cells (e.g. 'A1:C4') - :type range_string: string - - :param row: row index of the cell (e.g. 4) - :type row: int - - :param column: column index of the cell (e.g. 3) - :type column: int - - :rtype: generator - - """ - - return iter_rows(workbook_name = self._workbook_name, - sheet_name = self._sheet_codename, - xml_source = self._xml_source, - range_string = range_string, - row_offset = row_offset, - column_offset = column_offset) - - def cell(self, *args, **kwargs): - - raise NotImplementedError("use 'iter_rows()' instead") - - def range(self, *args, **kwargs): - - raise NotImplementedError("use 'iter_rows()' instead") - -def unpack_worksheet(archive, filename): - - temp_file = tempfile.TemporaryFile(mode='r+', prefix='openpyxl.', suffix='.unpack.temp') - - zinfo = archive.getinfo(filename) - - if zinfo.compress_type == zipfile.ZIP_STORED: - decoder = None - elif zinfo.compress_type == zipfile.ZIP_DEFLATED: - decoder = zlib.decompressobj(-zlib.MAX_WBITS) - else: - raise zipfile.BadZipFile("Unrecognized compression method") - - archive.fp.seek(_get_file_offset(archive, zinfo)) - bytes_to_read = zinfo.compress_size - - while True: - buff = archive.fp.read(min(bytes_to_read, 102400)) - if not buff: - break - bytes_to_read -= len(buff) - if decoder: - buff = decoder.decompress(buff) - temp_file.write(buff) - - if decoder: - temp_file.write(decoder.decompress('Z')) - - return temp_file - -def _get_file_offset(archive, zinfo): - - try: - return zinfo.file_offset - except AttributeError: - # From http://stackoverflow.com/questions/3781261/how-to-simulate-zipfile-open-in-python-2-5 - - # Seek over the fixed size fields to the "file name length" field in - # the file header (26 bytes). Unpack this and the "extra field length" - # field ourselves as info.extra doesn't seem to be the correct length. - archive.fp.seek(zinfo.header_offset + 26) - file_name_len, extra_len = struct.unpack(" 10000: - msg = 'Year not supported by Excel: %s' % year - raise ValueError(msg) - if self.excel_base_date == self.CALENDAR_WINDOWS_1900: - # Fudge factor for the erroneous fact that the year 1900 is - # treated as a Leap Year in MS Excel. This affects every date - # following 28th February 1900 - if year == 1900 and month <= 2: - excel_1900_leap_year = False - else: - excel_1900_leap_year = True - excel_base_date = 2415020 - else: - raise NotImplementedError('Mac dates are not yet supported.') - #excel_base_date = 2416481 - #excel_1900_leap_year = False - - # Julian base date adjustment - if month > 2: - month = month - 3 - else: - month = month + 9 - year -= 1 - - # Calculate the Julian Date, then subtract the Excel base date - # JD 2415020 = 31 - Dec - 1899 -> Excel Date of 0 - century, decade = int(str(year)[:2]), int(str(year)[2:]) - excel_date = floor(146097 * century / 4) + \ - floor((1461 * decade) / 4) + floor((153 * month + 2) / 5) + \ - day + 1721119 - excel_base_date - if excel_1900_leap_year: - excel_date += 1 - - # check to ensure that we exclude 2/29/1900 as a possible value - if self.excel_base_date == self.CALENDAR_WINDOWS_1900 \ - and excel_date == 60: - msg = 'Error: Excel believes 1900 was a leap year' - raise ValueError(msg) - excel_time = ((hours * 3600) + (minutes * 60) + seconds) / 86400 - return excel_date + excel_time - - def from_julian(self, value=0): - """Convert from the Excel JD back to a date""" - if self.excel_base_date == self.CALENDAR_WINDOWS_1900: - excel_base_date = 25569 - if value < 60: - excel_base_date -= 1 - elif value == 60: - msg = 'Error: Excel believes 1900 was a leap year' - raise ValueError(msg) - else: - raise NotImplementedError('Mac dates are not yet supported.') - #excel_base_date = 24107 - - if value >= 1: - utc_days = value - excel_base_date - - return EPOCH + datetime.timedelta(days=utc_days) - - elif value >= 0: - hours = floor(value * 24) - mins = floor(value * 24 * 60) - floor(hours * 60) - secs = floor(value * 24 * 60 * 60) - floor(hours * 60 * 60) - \ - floor(mins * 60) - return datetime.time(int(hours), int(mins), int(secs)) - else: - msg = 'Negative dates (%s) are not supported' % value - raise ValueError(msg) diff --git a/tablib/packages/openpyxl3/shared/exc.py b/tablib/packages/openpyxl3/shared/exc.py deleted file mode 100644 index 94a3e2c8..00000000 --- a/tablib/packages/openpyxl3/shared/exc.py +++ /dev/null @@ -1,59 +0,0 @@ -# file openpyxl/shared/exc.py - -# Copyright (c) 2010 openpyxl -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -# @license: http://www.opensource.org/licenses/mit-license.php -# @author: Eric Gazoni - -"""Definitions for openpyxl shared exception classes.""" - - -class CellCoordinatesException(Exception): - """Error for converting between numeric and A1-style cell references.""" - -class ColumnStringIndexException(Exception): - """Error for bad column names in A1-style cell references.""" - -class DataTypeException(Exception): - """Error for any data type inconsistencies.""" - -class NamedRangeException(Exception): - """Error for badly formatted named ranges.""" - -class SheetTitleException(Exception): - """Error for bad sheet names.""" - -class InsufficientCoordinatesException(Exception): - """Error for partially specified cell coordinates.""" - -class OpenModeError(Exception): - """Error for fileobj opened in non-binary mode.""" - -class InvalidFileException(Exception): - """Error for trying to open a non-ooxml file.""" - -class ReadOnlyWorkbookException(Exception): - """Error for trying to modify a read-only workbook""" - -class MissingNumberFormat(Exception): - """Error when a referenced number format is not in the stylesheet""" - - diff --git a/tablib/packages/openpyxl3/shared/ooxml.py b/tablib/packages/openpyxl3/shared/ooxml.py deleted file mode 100644 index 979b1724..00000000 --- a/tablib/packages/openpyxl3/shared/ooxml.py +++ /dev/null @@ -1,60 +0,0 @@ -# file openpyxl/shared/ooxml.py - -# Copyright (c) 2010 openpyxl -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -# @license: http://www.opensource.org/licenses/mit-license.php -# @author: Eric Gazoni - -"""Constants for fixed paths in a file and xml namespace urls.""" - -MIN_ROW = 0 -MIN_COLUMN = 0 -MAX_COLUMN = 16384 -MAX_ROW = 1048576 - -# constants -PACKAGE_PROPS = 'docProps' -PACKAGE_XL = 'xl' -PACKAGE_RELS = '_rels' -PACKAGE_THEME = PACKAGE_XL + '/' + 'theme' -PACKAGE_WORKSHEETS = PACKAGE_XL + '/' + 'worksheets' -PACKAGE_DRAWINGS = PACKAGE_XL + '/' + 'drawings' -PACKAGE_CHARTS = PACKAGE_XL + '/' + 'charts' - -ARC_CONTENT_TYPES = '[Content_Types].xml' -ARC_ROOT_RELS = PACKAGE_RELS + '/.rels' -ARC_WORKBOOK_RELS = PACKAGE_XL + '/' + PACKAGE_RELS + '/workbook.xml.rels' -ARC_CORE = PACKAGE_PROPS + '/core.xml' -ARC_APP = PACKAGE_PROPS + '/app.xml' -ARC_WORKBOOK = PACKAGE_XL + '/workbook.xml' -ARC_STYLE = PACKAGE_XL + '/styles.xml' -ARC_THEME = PACKAGE_THEME + '/theme1.xml' -ARC_SHARED_STRINGS = PACKAGE_XL + '/sharedStrings.xml' - -NAMESPACES = { - 'cp': 'http://schemas.openxmlformats.org/package/2006/metadata/core-properties', - 'dc': 'http://purl.org/dc/elements/1.1/', - 'dcterms': 'http://purl.org/dc/terms/', - 'dcmitype': 'http://purl.org/dc/dcmitype/', - 'xsi': 'http://www.w3.org/2001/XMLSchema-instance', - 'vt': 'http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes', - 'xml': 'http://www.w3.org/XML/1998/namespace' -} diff --git a/tablib/packages/openpyxl3/shared/password_hasher.py b/tablib/packages/openpyxl3/shared/password_hasher.py deleted file mode 100644 index b5d0dd09..00000000 --- a/tablib/packages/openpyxl3/shared/password_hasher.py +++ /dev/null @@ -1,47 +0,0 @@ -# file openpyxl/shared/password_hasher.py - -# Copyright (c) 2010 openpyxl -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -# @license: http://www.opensource.org/licenses/mit-license.php -# @author: Eric Gazoni - -"""Basic password hashing.""" - - -def hash_password(plaintext_password=''): - """Create a password hash from a given string. - - This method is based on the algorithm provided by - Daniel Rentz of OpenOffice and the PEAR package - Spreadsheet_Excel_Writer by Xavier Noguer . - - """ - password = 0x0000 - i = 1 - for char in plaintext_password: - value = ord(char) << i - rotated_bits = value >> 15 - value &= 0x7fff - password ^= (value | rotated_bits) - i += 1 - password ^= len(plaintext_password) - password ^= 0xCE4B - return str(hex(password)).upper()[2:] diff --git a/tablib/packages/openpyxl3/shared/units.py b/tablib/packages/openpyxl3/shared/units.py deleted file mode 100644 index fba82d70..00000000 --- a/tablib/packages/openpyxl3/shared/units.py +++ /dev/null @@ -1,67 +0,0 @@ -# file openpyxl/shared/units.py - -# Copyright (c) 2010 openpyxl -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -# @license: http://www.opensource.org/licenses/mit-license.php -# @author: Eric Gazoni - -import math - -def pixels_to_EMU(value): - return int(round(value * 9525)) - -def EMU_to_pixels(value): - if not value: - return 0 - else: - return round(value / 9525.) - -def EMU_to_cm(value): - if not value: - return 0 - else: - return (EMU_to_pixels(value) * 2.57 / 96) - -def pixels_to_points(value): - return value * 0.67777777 - -def points_to_pixels(value): - if not value: - return 0 - else: - return int(math.ceil(value * 1.333333333)) - -def degrees_to_angle(value): - return int(round(value * 60000)) - -def angle_to_degrees(value): - if not value: - return 0 - else: - return round(value / 60000.) - -def short_color(color): - """ format a color to its short size """ - - if len(color) > 6: - return color[2:] - else: - return color diff --git a/tablib/packages/openpyxl3/shared/xmltools.py b/tablib/packages/openpyxl3/shared/xmltools.py deleted file mode 100644 index 3c0be4c0..00000000 --- a/tablib/packages/openpyxl3/shared/xmltools.py +++ /dev/null @@ -1,96 +0,0 @@ -# file openpyxl/shared/xmltools.py - -# Copyright (c) 2010 openpyxl -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -# @license: http://www.opensource.org/licenses/mit-license.php -# @author: Eric Gazoni - -"""Shared xml tools. - -Shortcut functions taken from: - http://lethain.com/entry/2009/jan/22/handling-very-large-csv-and-xml-files-in-python/ - -""" - -# Python stdlib imports -from xml.sax.xmlreader import AttributesNSImpl -from xml.sax.saxutils import XMLGenerator -try: - from xml.etree.ElementTree import ElementTree, Element, SubElement, \ - QName, fromstring, tostring -except ImportError: - from cElementTree import ElementTree, Element, SubElement, \ - QName, fromstring, tostring - -# package imports -from .. import __name__ as prefix - - -def get_document_content(xml_node): - """Print nicely formatted xml to a string.""" - pretty_indent(xml_node) - return tostring(xml_node, 'utf-8') - - -def pretty_indent(elem, level=0): - """Format xml with nice indents and line breaks.""" - i = "\n" + level * " " - if len(elem): - if not elem.text or not elem.text.strip(): - elem.text = i + " " - if not elem.tail or not elem.tail.strip(): - elem.tail = i - for elem in elem: - pretty_indent(elem, level + 1) - if not elem.tail or not elem.tail.strip(): - elem.tail = i - else: - if level and (not elem.tail or not elem.tail.strip()): - elem.tail = i - - -def start_tag(doc, name, attr=None, body=None, namespace=None): - """Wrapper to start an xml tag.""" - if attr is None: - attr = {} - attr_vals = {} - attr_keys = {} - for key, val in attr.items(): - key_tuple = (namespace, key) - attr_vals[key_tuple] = val - attr_keys[key_tuple] = key - attr2 = AttributesNSImpl(attr_vals, attr_keys) - doc.startElementNS((namespace, name), name, attr2) - if body: - doc.characters(body) - - -def end_tag(doc, name, namespace=None): - """Wrapper to close an xml tag.""" - doc.endElementNS((namespace, name), name) - - -def tag(doc, name, attr=None, body=None, namespace=None): - """Wrapper to print xml tags and comments.""" - if attr is None: - attr = {} - start_tag(doc, name, attr, body, namespace) - end_tag(doc, name, namespace) diff --git a/tablib/packages/openpyxl3/style.py b/tablib/packages/openpyxl3/style.py deleted file mode 100644 index c113bd96..00000000 --- a/tablib/packages/openpyxl3/style.py +++ /dev/null @@ -1,392 +0,0 @@ -# file openpyxl/style.py - -# Copyright (c) 2010 openpyxl -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -# @license: http://www.opensource.org/licenses/mit-license.php -# @author: Eric Gazoni - -"""Style and formatting option tracking.""" - -# Python stdlib imports -import re -try: - from hashlib import md5 -except ImportError: - from md5 import md5 - - -class HashableObject(object): - """Define how to hash property classes.""" - __fields__ = None - __leaf__ = False - - def __repr__(self): - - return ':'.join([repr(getattr(self, x)) for x in self.__fields__]) - - def __hash__(self): - -# return int(md5(repr(self)).hexdigest(), 16) - return hash(repr(self)) - -class Color(HashableObject): - """Named colors for use in styles.""" - BLACK = 'FF000000' - WHITE = 'FFFFFFFF' - RED = 'FFFF0000' - DARKRED = 'FF800000' - BLUE = 'FF0000FF' - DARKBLUE = 'FF000080' - GREEN = 'FF00FF00' - DARKGREEN = 'FF008000' - YELLOW = 'FFFFFF00' - DARKYELLOW = 'FF808000' - - __fields__ = ('index',) - __slots__ = __fields__ - __leaf__ = True - - def __init__(self, index): - super(Color, self).__init__() - self.index = index - - -class Font(HashableObject): - """Font options used in styles.""" - UNDERLINE_NONE = 'none' - UNDERLINE_DOUBLE = 'double' - UNDERLINE_DOUBLE_ACCOUNTING = 'doubleAccounting' - UNDERLINE_SINGLE = 'single' - UNDERLINE_SINGLE_ACCOUNTING = 'singleAccounting' - - __fields__ = ('name', - 'size', - 'bold', - 'italic', - 'superscript', - 'subscript', - 'underline', - 'strikethrough', - 'color') - __slots__ = __fields__ - - def __init__(self): - super(Font, self).__init__() - self.name = 'Calibri' - self.size = 11 - self.bold = False - self.italic = False - self.superscript = False - self.subscript = False - self.underline = self.UNDERLINE_NONE - self.strikethrough = False - self.color = Color(Color.BLACK) - - -class Fill(HashableObject): - """Area fill patterns for use in styles.""" - FILL_NONE = 'none' - FILL_SOLID = 'solid' - FILL_GRADIENT_LINEAR = 'linear' - FILL_GRADIENT_PATH = 'path' - FILL_PATTERN_DARKDOWN = 'darkDown' - FILL_PATTERN_DARKGRAY = 'darkGray' - FILL_PATTERN_DARKGRID = 'darkGrid' - FILL_PATTERN_DARKHORIZONTAL = 'darkHorizontal' - FILL_PATTERN_DARKTRELLIS = 'darkTrellis' - FILL_PATTERN_DARKUP = 'darkUp' - FILL_PATTERN_DARKVERTICAL = 'darkVertical' - FILL_PATTERN_GRAY0625 = 'gray0625' - FILL_PATTERN_GRAY125 = 'gray125' - FILL_PATTERN_LIGHTDOWN = 'lightDown' - FILL_PATTERN_LIGHTGRAY = 'lightGray' - FILL_PATTERN_LIGHTGRID = 'lightGrid' - FILL_PATTERN_LIGHTHORIZONTAL = 'lightHorizontal' - FILL_PATTERN_LIGHTTRELLIS = 'lightTrellis' - FILL_PATTERN_LIGHTUP = 'lightUp' - FILL_PATTERN_LIGHTVERTICAL = 'lightVertical' - FILL_PATTERN_MEDIUMGRAY = 'mediumGray' - - __fields__ = ('fill_type', - 'rotation', - 'start_color', - 'end_color') - __slots__ = __fields__ - - def __init__(self): - super(Fill, self).__init__() - self.fill_type = self.FILL_NONE - self.rotation = 0 - self.start_color = Color(Color.WHITE) - self.end_color = Color(Color.BLACK) - - -class Border(HashableObject): - """Border options for use in styles.""" - BORDER_NONE = 'none' - BORDER_DASHDOT = 'dashDot' - BORDER_DASHDOTDOT = 'dashDotDot' - BORDER_DASHED = 'dashed' - BORDER_DOTTED = 'dotted' - BORDER_DOUBLE = 'double' - BORDER_HAIR = 'hair' - BORDER_MEDIUM = 'medium' - BORDER_MEDIUMDASHDOT = 'mediumDashDot' - BORDER_MEDIUMDASHDOTDOT = 'mediumDashDotDot' - BORDER_MEDIUMDASHED = 'mediumDashed' - BORDER_SLANTDASHDOT = 'slantDashDot' - BORDER_THICK = 'thick' - BORDER_THIN = 'thin' - - __fields__ = ('border_style', - 'color') - __slots__ = __fields__ - - def __init__(self): - super(Border, self).__init__() - self.border_style = self.BORDER_NONE - self.color = Color(Color.BLACK) - - -class Borders(HashableObject): - """Border positioning for use in styles.""" - DIAGONAL_NONE = 0 - DIAGONAL_UP = 1 - DIAGONAL_DOWN = 2 - DIAGONAL_BOTH = 3 - - __fields__ = ('left', - 'right', - 'top', - 'bottom', - 'diagonal', - 'diagonal_direction', - 'all_borders', - 'outline', - 'inside', - 'vertical', - 'horizontal') - __slots__ = __fields__ - - def __init__(self): - super(Borders, self).__init__() - self.left = Border() - self.right = Border() - self.top = Border() - self.bottom = Border() - self.diagonal = Border() - self.diagonal_direction = self.DIAGONAL_NONE - - self.all_borders = Border() - self.outline = Border() - self.inside = Border() - self.vertical = Border() - self.horizontal = Border() - - -class Alignment(HashableObject): - """Alignment options for use in styles.""" - HORIZONTAL_GENERAL = 'general' - HORIZONTAL_LEFT = 'left' - HORIZONTAL_RIGHT = 'right' - HORIZONTAL_CENTER = 'center' - HORIZONTAL_CENTER_CONTINUOUS = 'centerContinuous' - HORIZONTAL_JUSTIFY = 'justify' - VERTICAL_BOTTOM = 'bottom' - VERTICAL_TOP = 'top' - VERTICAL_CENTER = 'center' - VERTICAL_JUSTIFY = 'justify' - - __fields__ = ('horizontal', - 'vertical', - 'text_rotation', - 'wrap_text', - 'shrink_to_fit', - 'indent') - __slots__ = __fields__ - __leaf__ = True - - def __init__(self): - super(Alignment, self).__init__() - self.horizontal = self.HORIZONTAL_GENERAL - self.vertical = self.VERTICAL_BOTTOM - self.text_rotation = 0 - self.wrap_text = False - self.shrink_to_fit = False - self.indent = 0 - - -class NumberFormat(HashableObject): - """Numer formatting for use in styles.""" - FORMAT_GENERAL = 'General' - FORMAT_TEXT = '@' - FORMAT_NUMBER = '0' - FORMAT_NUMBER_00 = '0.00' - FORMAT_NUMBER_COMMA_SEPARATED1 = '#,##0.00' - FORMAT_NUMBER_COMMA_SEPARATED2 = '#,##0.00_-' - FORMAT_PERCENTAGE = '0%' - FORMAT_PERCENTAGE_00 = '0.00%' - FORMAT_DATE_YYYYMMDD2 = 'yyyy-mm-dd' - FORMAT_DATE_YYYYMMDD = 'yy-mm-dd' - FORMAT_DATE_DDMMYYYY = 'dd/mm/yy' - FORMAT_DATE_DMYSLASH = 'd/m/y' - FORMAT_DATE_DMYMINUS = 'd-m-y' - FORMAT_DATE_DMMINUS = 'd-m' - FORMAT_DATE_MYMINUS = 'm-y' - FORMAT_DATE_XLSX14 = 'mm-dd-yy' - FORMAT_DATE_XLSX15 = 'd-mmm-yy' - FORMAT_DATE_XLSX16 = 'd-mmm' - FORMAT_DATE_XLSX17 = 'mmm-yy' - FORMAT_DATE_XLSX22 = 'm/d/yy h:mm' - FORMAT_DATE_DATETIME = 'd/m/y h:mm' - FORMAT_DATE_TIME1 = 'h:mm AM/PM' - FORMAT_DATE_TIME2 = 'h:mm:ss AM/PM' - FORMAT_DATE_TIME3 = 'h:mm' - FORMAT_DATE_TIME4 = 'h:mm:ss' - FORMAT_DATE_TIME5 = 'mm:ss' - FORMAT_DATE_TIME6 = 'h:mm:ss' - FORMAT_DATE_TIME7 = 'i:s.S' - FORMAT_DATE_TIME8 = 'h:mm:ss@' - FORMAT_DATE_YYYYMMDDSLASH = 'yy/mm/dd@' - FORMAT_CURRENCY_USD_SIMPLE = '"$"#,##0.00_-' - FORMAT_CURRENCY_USD = '$#,##0_-' - FORMAT_CURRENCY_EUR_SIMPLE = '[$EUR ]#,##0.00_-' - _BUILTIN_FORMATS = { - 0: 'General', - 1: '0', - 2: '0.00', - 3: '#,##0', - 4: '#,##0.00', - - 9: '0%', - 10: '0.00%', - 11: '0.00E+00', - 12: '# ?/?', - 13: '# ??/??', - 14: 'mm-dd-yy', - 15: 'd-mmm-yy', - 16: 'd-mmm', - 17: 'mmm-yy', - 18: 'h:mm AM/PM', - 19: 'h:mm:ss AM/PM', - 20: 'h:mm', - 21: 'h:mm:ss', - 22: 'm/d/yy h:mm', - - 37: '#,##0 (#,##0)', - 38: '#,##0 [Red](#,##0)', - 39: '#,##0.00(#,##0.00)', - 40: '#,##0.00[Red](#,##0.00)', - - 41: '_(* #,##0_);_(* \(#,##0\);_(* "-"_);_(@_)', - 42: '_("$"* #,##0_);_("$"* \(#,##0\);_("$"* "-"_);_(@_)', - 43: '_(* #,##0.00_);_(* \(#,##0.00\);_(* "-"??_);_(@_)', - - 44: '_("$"* #,##0.00_)_("$"* \(#,##0.00\)_("$"* "-"??_)_(@_)', - 45: 'mm:ss', - 46: '[h]:mm:ss', - 47: 'mmss.0', - 48: '##0.0E+0', - 49: '@', } - _BUILTIN_FORMATS_REVERSE = dict( - [(value, key) for key, value in _BUILTIN_FORMATS.items()]) - - __fields__ = ('_format_code', - '_format_index') - __slots__ = __fields__ - __leaf__ = True - - DATE_INDICATORS = 'dmyhs' - - def __init__(self): - super(NumberFormat, self).__init__() - self._format_code = self.FORMAT_GENERAL - self._format_index = 0 - - def _set_format_code(self, format_code = FORMAT_GENERAL): - """Setter for the format_code property.""" - self._format_code = format_code - self._format_index = self.builtin_format_id(format = format_code) - - def _get_format_code(self): - """Getter for the format_code property.""" - return self._format_code - - format_code = property(_get_format_code, _set_format_code) - - def builtin_format_code(self, index): - """Return one of the standard format codes by index.""" - return self._BUILTIN_FORMATS[index] - - def is_builtin(self, format = None): - """Check if a format code is a standard format code.""" - if format is None: - format = self._format_code - return format in list(self._BUILTIN_FORMATS.values()) - - def builtin_format_id(self, format): - """Return the id of a standard style.""" - return self._BUILTIN_FORMATS_REVERSE.get(format, None) - - def is_date_format(self, format = None): - """Check if the number format is actually representing a date.""" - if format is None: - format = self._format_code - - return any([x in format for x in self.DATE_INDICATORS]) - -class Protection(HashableObject): - """Protection options for use in styles.""" - PROTECTION_INHERIT = 'inherit' - PROTECTION_PROTECTED = 'protected' - PROTECTION_UNPROTECTED = 'unprotected' - - __fields__ = ('locked', - 'hidden') - __slots__ = __fields__ - __leaf__ = True - - def __init__(self): - super(Protection, self).__init__() - self.locked = self.PROTECTION_INHERIT - self.hidden = self.PROTECTION_INHERIT - - -class Style(HashableObject): - """Style object containing all formatting details.""" - __fields__ = ('font', - 'fill', - 'borders', - 'alignment', - 'number_format', - 'protection') - __slots__ = __fields__ - - def __init__(self): - super(Style, self).__init__() - self.font = Font() - self.fill = Fill() - self.borders = Borders() - self.alignment = Alignment() - self.number_format = NumberFormat() - self.protection = Protection() - -DEFAULTS = Style() diff --git a/tablib/packages/openpyxl3/workbook.py b/tablib/packages/openpyxl3/workbook.py deleted file mode 100644 index bbb14b62..00000000 --- a/tablib/packages/openpyxl3/workbook.py +++ /dev/null @@ -1,186 +0,0 @@ -# file openpyxl/workbook.py - -# Copyright (c) 2010 openpyxl -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -# @license: http://www.opensource.org/licenses/mit-license.php -# @author: Eric Gazoni - -"""Workbook is the top-level container for all document information.""" - -__docformat__ = "restructuredtext en" - -# Python stdlib imports -import datetime -import os - -# package imports -from .worksheet import Worksheet -from .writer.dump_worksheet import DumpWorksheet, save_dump -from .writer.strings import StringTableBuilder -from .namedrange import NamedRange -from .style import Style -from .writer.excel import save_workbook -from .shared.exc import ReadOnlyWorkbookException - - -class DocumentProperties(object): - """High-level properties of the document.""" - - def __init__(self): - self.creator = 'Unknown' - self.last_modified_by = self.creator - self.created = datetime.datetime.now() - self.modified = datetime.datetime.now() - self.title = 'Untitled' - self.subject = '' - self.description = '' - self.keywords = '' - self.category = '' - self.company = 'Microsoft Corporation' - - -class DocumentSecurity(object): - """Security information about the document.""" - - def __init__(self): - self.lock_revision = False - self.lock_structure = False - self.lock_windows = False - self.revision_password = '' - self.workbook_password = '' - - -class Workbook(object): - """Workbook is the container for all other parts of the document.""" - - def __init__(self, optimized_write = False): - self.worksheets = [] - self._active_sheet_index = 0 - self._named_ranges = [] - self.properties = DocumentProperties() - self.style = Style() - self.security = DocumentSecurity() - self.__optimized_write = optimized_write - self.__optimized_read = False - self.strings_table_builder = StringTableBuilder() - - if not optimized_write: - self.worksheets.append(Worksheet(self)) - - def _set_optimized_read(self): - self.__optimized_read = True - - def get_active_sheet(self): - """Returns the current active sheet.""" - return self.worksheets[self._active_sheet_index] - - def create_sheet(self, index = None): - """Create a worksheet (at an optional index). - - :param index: optional position at which the sheet will be inserted - :type index: int - - """ - - if self.__optimized_read: - raise ReadOnlyWorkbookException('Cannot create new sheet in a read-only workbook') - - if self.__optimized_write : - new_ws = DumpWorksheet(parent_workbook = self) - else: - new_ws = Worksheet(parent_workbook = self) - - self.add_sheet(worksheet = new_ws, index = index) - return new_ws - - def add_sheet(self, worksheet, index = None): - """Add an existing worksheet (at an optional index).""" - if index is None: - index = len(self.worksheets) - self.worksheets.insert(index, worksheet) - - def remove_sheet(self, worksheet): - """Remove a worksheet from this workbook.""" - self.worksheets.remove(worksheet) - - def get_sheet_by_name(self, name): - """Returns a worksheet by its name. - - Returns None if no worksheet has the name specified. - - :param name: the name of the worksheet to look for - :type name: string - - """ - requested_sheet = None - for sheet in self.worksheets: - if sheet.title == name: - requested_sheet = sheet - break - return requested_sheet - - def get_index(self, worksheet): - """Return the index of the worksheet.""" - return self.worksheets.index(worksheet) - - def get_sheet_names(self): - """Returns the list of the names of worksheets in the workbook. - - Names are returned in the worksheets order. - - :rtype: list of strings - - """ - return [s.title for s in self.worksheets] - - def create_named_range(self, name, worksheet, range): - """Create a new named_range on a worksheet""" - assert isinstance(worksheet, Worksheet) - named_range = NamedRange(name, [(worksheet, range)]) - self.add_named_range(named_range) - - def get_named_ranges(self): - """Return all named ranges""" - return self._named_ranges - - def add_named_range(self, named_range): - """Add an existing named_range to the list of named_ranges.""" - self._named_ranges.append(named_range) - - def get_named_range(self, name): - """Return the range specified by name.""" - requested_range = None - for named_range in self._named_ranges: - if named_range.name == name: - requested_range = named_range - break - return requested_range - - def remove_named_range(self, named_range): - """Remove a named_range from this workbook.""" - self._named_ranges.remove(named_range) - - def save(self, filename): - """ shortcut """ - if self.__optimized_write: - save_dump(self, filename) - else: - save_workbook(self, filename) diff --git a/tablib/packages/openpyxl3/worksheet.py b/tablib/packages/openpyxl3/worksheet.py deleted file mode 100644 index 6cdda6b4..00000000 --- a/tablib/packages/openpyxl3/worksheet.py +++ /dev/null @@ -1,534 +0,0 @@ -# file openpyxl/worksheet.py - -# Copyright (c) 2010 openpyxl -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -# @license: http://www.opensource.org/licenses/mit-license.php -# @author: Eric Gazoni - -"""Worksheet is the 2nd-level container in Excel.""" - -# Python stdlib imports -import re - -# package imports -from . import cell -from .cell import coordinate_from_string, \ - column_index_from_string, get_column_letter -from .shared.exc import SheetTitleException, \ - InsufficientCoordinatesException, CellCoordinatesException, \ - NamedRangeException -from .shared.password_hasher import hash_password -from .style import Style, DEFAULTS as DEFAULTS_STYLE -from .drawing import Drawing - -_DEFAULTS_STYLE_HASH = hash(DEFAULTS_STYLE) - -def flatten(results): - - rows = [] - - for row in results: - - cells = [] - - for cell in row: - - cells.append(cell.value) - - rows.append(tuple(cells)) - - return tuple(rows) - - -class Relationship(object): - """Represents many kinds of relationships.""" - # TODO: Use this object for workbook relationships as well as - # worksheet relationships - TYPES = { - 'hyperlink': 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink', - 'drawing':'http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing', - #'worksheet': 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet', - #'sharedStrings': 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings', - #'styles': 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles', - #'theme': 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme', - } - - def __init__(self, rel_type): - if rel_type not in self.TYPES: - raise ValueError("Invalid relationship type %s" % rel_type) - self.type = self.TYPES[rel_type] - self.target = "" - self.target_mode = "" - self.id = "" - - -class PageSetup(object): - """Information about page layout for this sheet""" - pass - - -class HeaderFooter(object): - """Information about the header/footer for this sheet.""" - pass - - -class SheetView(object): - """Information about the visible portions of this sheet.""" - pass - - -class RowDimension(object): - """Information about the display properties of a row.""" - __slots__ = ('row_index', - 'height', - 'visible', - 'outline_level', - 'collapsed', - 'style_index',) - - def __init__(self, index = 0): - self.row_index = index - self.height = -1 - self.visible = True - self.outline_level = 0 - self.collapsed = False - self.style_index = None - - -class ColumnDimension(object): - """Information about the display properties of a column.""" - __slots__ = ('column_index', - 'width', - 'auto_size', - 'visible', - 'outline_level', - 'collapsed', - 'style_index',) - - def __init__(self, index = 'A'): - self.column_index = index - self.width = -1 - self.auto_size = False - self.visible = True - self.outline_level = 0 - self.collapsed = False - self.style_index = 0 - - -class PageMargins(object): - """Information about page margins for view/print layouts.""" - - def __init__(self): - self.left = self.right = 0.7 - self.top = self.bottom = 0.75 - self.header = self.footer = 0.3 - - -class SheetProtection(object): - """Information about protection of various aspects of a sheet.""" - - def __init__(self): - self.sheet = False - self.objects = False - self.scenarios = False - self.format_cells = False - self.format_columns = False - self.format_rows = False - self.insert_columns = False - self.insert_rows = False - self.insert_hyperlinks = False - self.delete_columns = False - self.delete_rows = False - self.select_locked_cells = False - self.sort = False - self.auto_filter = False - self.pivot_tables = False - self.select_unlocked_cells = False - self._password = '' - - def set_password(self, value = '', already_hashed = False): - """Set a password on this sheet.""" - if not already_hashed: - value = hash_password(value) - self._password = value - - def _set_raw_password(self, value): - """Set a password directly, forcing a hash step.""" - self.set_password(value, already_hashed = False) - - def _get_raw_password(self): - """Return the password value, regardless of hash.""" - return self._password - - password = property(_get_raw_password, _set_raw_password, - 'get/set the password (if already hashed, ' - 'use set_password() instead)') - - -class Worksheet(object): - """Represents a worksheet. - - Do not create worksheets yourself, - use :func:`.workbook.Workbook.create_sheet` instead - - """ - BREAK_NONE = 0 - BREAK_ROW = 1 - BREAK_COLUMN = 2 - - SHEETSTATE_VISIBLE = 'visible' - SHEETSTATE_HIDDEN = 'hidden' - SHEETSTATE_VERYHIDDEN = 'veryHidden' - - def __init__(self, parent_workbook, title = 'Sheet'): - self._parent = parent_workbook - self._title = '' - if not title: - self.title = 'Sheet%d' % (1 + len(self._parent.worksheets)) - else: - self.title = title - self.row_dimensions = {} - self.column_dimensions = {} - self._cells = {} - self._styles = {} - self._charts = [] - self.relationships = [] - self.selected_cell = 'A1' - self.active_cell = 'A1' - self.sheet_state = self.SHEETSTATE_VISIBLE - self.page_setup = PageSetup() - self.page_margins = PageMargins() - self.header_footer = HeaderFooter() - self.sheet_view = SheetView() - self.protection = SheetProtection() - self.show_gridlines = True - self.print_gridlines = False - self.show_summary_below = True - self.show_summary_right = True - self.default_row_dimension = RowDimension() - self.default_column_dimension = ColumnDimension() - self._auto_filter = None - self._freeze_panes = None - - def __repr__(self): - return '' % self.title - - def garbage_collect(self): - """Delete cells that are not storing a value.""" - delete_list = [coordinate for coordinate, cell in \ - self._cells.items() if (cell.value in ('', None) and \ - hash(cell.style) == _DEFAULTS_STYLE_HASH)] - for coordinate in delete_list: - del self._cells[coordinate] - - def get_cell_collection(self): - """Return an unordered list of the cells in this worksheet.""" - return list(self._cells.values()) - - def _set_title(self, value): - """Set a sheet title, ensuring it is valid.""" - bad_title_char_re = re.compile(r'[\\*?:/\[\]]') - if bad_title_char_re.search(value): - msg = 'Invalid character found in sheet title' - raise SheetTitleException(msg) - - # check if sheet_name already exists - # do this *before* length check - if self._parent.get_sheet_by_name(value): - # use name, but append with lowest possible integer - i = 1 - while self._parent.get_sheet_by_name('%s%d' % (value, i)): - i += 1 - value = '%s%d' % (value, i) - if len(value) > 31: - msg = 'Maximum 31 characters allowed in sheet title' - raise SheetTitleException(msg) - self._title = value - - def _get_title(self): - """Return the title for this sheet.""" - return self._title - - title = property(_get_title, _set_title, doc = - 'Get or set the title of the worksheet. ' - 'Limited to 31 characters, no special characters.') - - def _set_auto_filter(self, range): - # Normalize range to a str or None - if not range: - range = None - elif isinstance(range, str): - range = range.upper() - else: # Assume a range - range = range[0][0].address + ':' + range[-1][-1].address - self._auto_filter = range - - def _get_auto_filter(self): - return self._auto_filter - - auto_filter = property(_get_auto_filter, _set_auto_filter, doc = - 'get or set auto filtering on columns') - def _set_freeze_panes(self, topLeftCell): - if not topLeftCell: - topLeftCell = None - elif isinstance(topLeftCell, str): - topLeftCell = topLeftCell.upper() - else: # Assume a cell - topLeftCell = topLeftCell.address - if topLeftCell == 'A1': - topLeftCell = None - self._freeze_panes = topLeftCell - - def _get_freeze_panes(self): - return self._freeze_panes - - freeze_panes = property(_get_freeze_panes,_set_freeze_panes, doc = - "Get or set frozen panes") - - def cell(self, coordinate = None, row = None, column = None): - """Returns a cell object based on the given coordinates. - - Usage: cell(coodinate='A15') **or** cell(row=15, column=1) - - If `coordinates` are not given, then row *and* column must be given. - - Cells are kept in a dictionary which is empty at the worksheet - creation. Calling `cell` creates the cell in memory when they - are first accessed, to reduce memory usage. - - :param coordinate: coordinates of the cell (e.g. 'B12') - :type coordinate: string - - :param row: row index of the cell (e.g. 4) - :type row: int - - :param column: column index of the cell (e.g. 3) - :type column: int - - :raise: InsufficientCoordinatesException when coordinate or (row and column) are not given - - :rtype: :class:`.cell.Cell` - - """ - if not coordinate: - if (row is None or column is None): - msg = "You have to provide a value either for " \ - "'coordinate' or for 'row' *and* 'column'" - raise InsufficientCoordinatesException(msg) - else: - coordinate = '%s%s' % (get_column_letter(column + 1), row + 1) - else: - coordinate = coordinate.replace('$', '') - - return self._get_cell(coordinate) - - def _get_cell(self, coordinate): - - if not coordinate in self._cells: - column, row = coordinate_from_string(coordinate) - new_cell = cell.Cell(self, column, row) - self._cells[coordinate] = new_cell - if column not in self.column_dimensions: - self.column_dimensions[column] = ColumnDimension(column) - if row not in self.row_dimensions: - self.row_dimensions[row] = RowDimension(row) - return self._cells[coordinate] - - def get_highest_row(self): - """Returns the maximum row index containing data - - :rtype: int - """ - if self.row_dimensions: - return max(self.row_dimensions.keys()) - else: - return 1 - - def get_highest_column(self): - """Get the largest value for column currently stored. - - :rtype: int - """ - if self.column_dimensions: - return max([column_index_from_string(column_index) - for column_index in self.column_dimensions]) - else: - return 1 - - def calculate_dimension(self): - """Return the minimum bounding range for all cells containing data.""" - return 'A1:%s%d' % (get_column_letter(self.get_highest_column()), - self.get_highest_row()) - - def range(self, range_string, row = 0, column = 0): - """Returns a 2D array of cells, with optional row and column offsets. - - :param range_string: cell range string or `named range` name - :type range_string: string - - :param row: number of rows to offset - :type row: int - - :param column: number of columns to offset - :type column: int - - :rtype: tuples of tuples of :class:`.cell.Cell` - - """ - if ':' in range_string: - # R1C1 range - result = [] - min_range, max_range = range_string.split(':') - min_col, min_row = coordinate_from_string(min_range) - max_col, max_row = coordinate_from_string(max_range) - if column: - min_col = get_column_letter( - column_index_from_string(min_col) + column) - max_col = get_column_letter( - column_index_from_string(max_col) + column) - min_col = column_index_from_string(min_col) - max_col = column_index_from_string(max_col) - cache_cols = {} - for col in range(min_col, max_col + 1): - cache_cols[col] = get_column_letter(col) - rows = range(min_row + row, max_row + row + 1) - cols = range(min_col, max_col + 1) - for row in rows: - new_row = [] - for col in cols: - new_row.append(self.cell('%s%s' % (cache_cols[col], row))) - result.append(tuple(new_row)) - return tuple(result) - else: - try: - return self.cell(coordinate = range_string, row = row, - column = column) - except CellCoordinatesException: - pass - - # named range - named_range = self._parent.get_named_range(range_string) - if named_range is None: - msg = '%s is not a valid range name' % range_string - raise NamedRangeException(msg) - - result = [] - for destination in named_range.destinations: - - worksheet, cells_range = destination - - if worksheet is not self: - msg = 'Range %s is not defined on worksheet %s' % \ - (cells_range, self.title) - raise NamedRangeException(msg) - - content = self.range(cells_range) - - if isinstance(content, tuple): - for cells in content: - result.extend(cells) - else: - result.append(content) - - if len(result) == 1: - return result[0] - else: - return tuple(result) - - def get_style(self, coordinate): - """Return the style object for the specified cell.""" - if not coordinate in self._styles: - self._styles[coordinate] = Style() - return self._styles[coordinate] - - def create_relationship(self, rel_type): - """Add a relationship for this sheet.""" - rel = Relationship(rel_type) - self.relationships.append(rel) - rel_id = self.relationships.index(rel) - rel.id = 'rId' + str(rel_id + 1) - return self.relationships[rel_id] - - def add_chart(self, chart): - """ Add a chart to the sheet """ - - chart._sheet = self - self._charts.append(chart) - - def append(self, list_or_dict): - """Appends a group of values at the bottom of the current sheet. - - * If it's a list: all values are added in order, starting from the first column - * If it's a dict: values are assigned to the columns indicated by the keys (numbers or letters) - - :param list_or_dict: list or dict containing values to append - :type list_or_dict: list/tuple or dict - - Usage: - - * append(['This is A1', 'This is B1', 'This is C1']) - * **or** append({'A' : 'This is A1', 'C' : 'This is C1'}) - * **or** append({0 : 'This is A1', 2 : 'This is C1'}) - - :raise: TypeError when list_or_dict is neither a list/tuple nor a dict - - """ - - row_idx = len(self.row_dimensions) - - if isinstance(list_or_dict, (list, tuple)): - - for col_idx, content in enumerate(list_or_dict): - - self.cell(row = row_idx, column = col_idx).value = content - - elif isinstance(list_or_dict, dict): - - for col_idx, content in list_or_dict.items(): - - if isinstance(col_idx, str): - col_idx = column_index_from_string(col_idx) - 1 - - self.cell(row = row_idx, column = col_idx).value = content - - else: - raise TypeError('list_or_dict must be a list or a dict') - - @property - def rows(self): - - return self.range(self.calculate_dimension()) - - @property - def columns(self): - - max_row = self.get_highest_row() - - cols = [] - - for col_idx in range(self.get_highest_column()): - col = get_column_letter(col_idx+1) - res = self.range('%s1:%s%d' % (col, col, max_row)) - cols.append(tuple([x[0] for x in res])) - - - return tuple(cols) - diff --git a/tablib/packages/openpyxl3/writer/__init__.py b/tablib/packages/openpyxl3/writer/__init__.py deleted file mode 100644 index 9eb0a218..00000000 --- a/tablib/packages/openpyxl3/writer/__init__.py +++ /dev/null @@ -1,34 +0,0 @@ -# file openpyxl/writer/__init__.py - -# Copyright (c) 2010 openpyxl -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -# @license: http://www.opensource.org/licenses/mit-license.php -# @author: Eric Gazoni - -"""Imports for the openpyxl.writer namespace.""" - -# package imports -from . import excel -from . import strings -from . import styles -from . import theme -from . import workbook -from . import worksheet diff --git a/tablib/packages/openpyxl3/writer/charts.py b/tablib/packages/openpyxl3/writer/charts.py deleted file mode 100644 index 420328da..00000000 --- a/tablib/packages/openpyxl3/writer/charts.py +++ /dev/null @@ -1,262 +0,0 @@ -# coding=UTF-8 -''' -Copyright (c) 2010 openpyxl - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - -@license: http://www.opensource.org/licenses/mit-license.php -@author: Eric Gazoni -''' - -from ..shared.xmltools import Element, SubElement, get_document_content -from ..chart import Chart, ErrorBar - - -class ChartWriter(object): - - def __init__(self, chart): - self.chart = chart - - def write(self): - """ write a chart """ - - root = Element('c:chartSpace', - {'xmlns:c':"http://schemas.openxmlformats.org/drawingml/2006/chart", - 'xmlns:a':"http://schemas.openxmlformats.org/drawingml/2006/main", - 'xmlns:r':"http://schemas.openxmlformats.org/officeDocument/2006/relationships"}) - - SubElement(root, 'c:lang', {'val':self.chart.lang}) - self._write_chart(root) - self._write_print_settings(root) - self._write_shapes(root) - - return get_document_content(root) - - def _write_chart(self, root): - - chart = self.chart - - ch = SubElement(root, 'c:chart') - self._write_title(ch) - plot_area = SubElement(ch, 'c:plotArea') - layout = SubElement(plot_area, 'c:layout') - mlayout = SubElement(layout, 'c:manualLayout') - SubElement(mlayout, 'c:layoutTarget', {'val':'inner'}) - SubElement(mlayout, 'c:xMode', {'val':'edge'}) - SubElement(mlayout, 'c:yMode', {'val':'edge'}) - SubElement(mlayout, 'c:x', {'val':str(chart._get_margin_left())}) - SubElement(mlayout, 'c:y', {'val':str(chart._get_margin_top())}) - SubElement(mlayout, 'c:w', {'val':str(chart.width)}) - SubElement(mlayout, 'c:h', {'val':str(chart.height)}) - - if chart.type == Chart.SCATTER_CHART: - subchart = SubElement(plot_area, 'c:scatterChart') - SubElement(subchart, 'c:scatterStyle', {'val':str('lineMarker')}) - else: - if chart.type == Chart.BAR_CHART: - subchart = SubElement(plot_area, 'c:barChart') - SubElement(subchart, 'c:barDir', {'val':'col'}) - else: - subchart = SubElement(plot_area, 'c:lineChart') - - SubElement(subchart, 'c:grouping', {'val':chart.grouping}) - - self._write_series(subchart) - - SubElement(subchart, 'c:marker', {'val':'1'}) - SubElement(subchart, 'c:axId', {'val':str(chart.x_axis.id)}) - SubElement(subchart, 'c:axId', {'val':str(chart.y_axis.id)}) - - if chart.type == Chart.SCATTER_CHART: - self._write_axis(plot_area, chart.x_axis, 'c:valAx') - else: - self._write_axis(plot_area, chart.x_axis, 'c:catAx') - self._write_axis(plot_area, chart.y_axis, 'c:valAx') - - self._write_legend(ch) - - SubElement(ch, 'c:plotVisOnly', {'val':'1'}) - - def _write_title(self, chart): - if self.chart.title != '': - title = SubElement(chart, 'c:title') - tx = SubElement(title, 'c:tx') - rich = SubElement(tx, 'c:rich') - SubElement(rich, 'a:bodyPr') - SubElement(rich, 'a:lstStyle') - p = SubElement(rich, 'a:p') - pPr = SubElement(p, 'a:pPr') - SubElement(pPr, 'a:defRPr') - r = SubElement(p, 'a:r') - SubElement(r, 'a:rPr', {'lang':self.chart.lang}) - t = SubElement(r, 'a:t').text = self.chart.title - SubElement(title, 'c:layout') - - def _write_axis(self, plot_area, axis, label): - - ax = SubElement(plot_area, label) - SubElement(ax, 'c:axId', {'val':str(axis.id)}) - - scaling = SubElement(ax, 'c:scaling') - SubElement(scaling, 'c:orientation', {'val':axis.orientation}) - if label == 'c:valAx': - SubElement(scaling, 'c:max', {'val':str(axis.max)}) - SubElement(scaling, 'c:min', {'val':str(axis.min)}) - - SubElement(ax, 'c:axPos', {'val':axis.position}) - if label == 'c:valAx': - SubElement(ax, 'c:majorGridlines') - SubElement(ax, 'c:numFmt', {'formatCode':"General", 'sourceLinked':'1'}) - SubElement(ax, 'c:tickLblPos', {'val':axis.tick_label_position}) - SubElement(ax, 'c:crossAx', {'val':str(axis.cross)}) - SubElement(ax, 'c:crosses', {'val':axis.crosses}) - if axis.auto: - SubElement(ax, 'c:auto', {'val':'1'}) - if axis.label_align: - SubElement(ax, 'c:lblAlgn', {'val':axis.label_align}) - if axis.label_offset: - SubElement(ax, 'c:lblOffset', {'val':str(axis.label_offset)}) - if label == 'c:valAx': - if self.chart.type == Chart.SCATTER_CHART: - SubElement(ax, 'c:crossBetween', {'val':'midCat'}) - else: - SubElement(ax, 'c:crossBetween', {'val':'between'}) - SubElement(ax, 'c:majorUnit', {'val':str(axis.unit)}) - - def _write_series(self, subchart): - - for i, serie in enumerate(self.chart._series): - ser = SubElement(subchart, 'c:ser') - SubElement(ser, 'c:idx', {'val':str(i)}) - SubElement(ser, 'c:order', {'val':str(i)}) - - if serie.legend: - tx = SubElement(ser, 'c:tx') - self._write_serial(tx, serie.legend) - - if serie.color: - sppr = SubElement(ser, 'c:spPr') - if self.chart.type == Chart.BAR_CHART: - # fill color - fillc = SubElement(sppr, 'a:solidFill') - SubElement(fillc, 'a:srgbClr', {'val':serie.color}) - # edge color - ln = SubElement(sppr, 'a:ln') - fill = SubElement(ln, 'a:solidFill') - SubElement(fill, 'a:srgbClr', {'val':serie.color}) - - if serie.error_bar: - self._write_error_bar(ser, serie) - - marker = SubElement(ser, 'c:marker') - SubElement(marker, 'c:symbol', {'val':serie.marker}) - - if serie.labels: - cat = SubElement(ser, 'c:cat') - self._write_serial(cat, serie.labels) - - if self.chart.type == Chart.SCATTER_CHART: - if serie.xvalues: - xval = SubElement(ser, 'c:xVal') - self._write_serial(xval, serie.xvalues) - - yval = SubElement(ser, 'c:yVal') - self._write_serial(yval, serie.values) - else: - val = SubElement(ser, 'c:val') - self._write_serial(val, serie.values) - - def _write_serial(self, node, serie, literal=False): - - cache = serie._get_cache() - if isinstance(cache[0], str): - typ = 'str' - else: - typ = 'num' - - if not literal: - if typ == 'num': - ref = SubElement(node, 'c:numRef') - else: - ref = SubElement(node, 'c:strRef') - SubElement(ref, 'c:f').text = serie._get_ref() - if typ == 'num': - data = SubElement(ref, 'c:numCache') - else: - data = SubElement(ref, 'c:strCache') - else: - data = SubElement(node, 'c:numLit') - - if typ == 'num': - SubElement(data, 'c:formatCode').text = 'General' - if literal: - values = (1,) - else: - values = cache - - SubElement(data, 'c:ptCount', {'val':str(len(values))}) - for j, val in enumerate(values): - point = SubElement(data, 'c:pt', {'idx':str(j)}) - SubElement(point, 'c:v').text = str(val) - - def _write_error_bar(self, node, serie): - - flag = {ErrorBar.PLUS_MINUS:'both', - ErrorBar.PLUS:'plus', - ErrorBar.MINUS:'minus'} - - eb = SubElement(node, 'c:errBars') - SubElement(eb, 'c:errBarType', {'val':flag[serie.error_bar.type]}) - SubElement(eb, 'c:errValType', {'val':'cust'}) - - plus = SubElement(eb, 'c:plus') - self._write_serial(plus, serie.error_bar.values, - literal=(serie.error_bar.type==ErrorBar.MINUS)) - - minus = SubElement(eb, 'c:minus') - self._write_serial(minus, serie.error_bar.values, - literal=(serie.error_bar.type==ErrorBar.PLUS)) - - def _write_legend(self, chart): - - legend = SubElement(chart, 'c:legend') - SubElement(legend, 'c:legendPos', {'val':self.chart.legend.position}) - SubElement(legend, 'c:layout') - - def _write_print_settings(self, root): - - settings = SubElement(root, 'c:printSettings') - SubElement(settings, 'c:headerFooter') - margins = dict([(k, str(v)) for (k,v) in self.chart.print_margins.items()]) - SubElement(settings, 'c:pageMargins', margins) - SubElement(settings, 'c:pageSetup') - - def _write_shapes(self, root): - - if self.chart._shapes: - SubElement(root, 'c:userShapes', {'r:id':'rId1'}) - - def write_rels(self, drawing_id): - - root = Element('Relationships', {'xmlns' : 'http://schemas.openxmlformats.org/package/2006/relationships'}) - attrs = {'Id' : 'rId1', - 'Type' : 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/chartUserShapes', - 'Target' : '../drawings/drawing%s.xml' % drawing_id } - SubElement(root, 'Relationship', attrs) - return get_document_content(root) diff --git a/tablib/packages/openpyxl3/writer/drawings.py b/tablib/packages/openpyxl3/writer/drawings.py deleted file mode 100644 index 8a6cce21..00000000 --- a/tablib/packages/openpyxl3/writer/drawings.py +++ /dev/null @@ -1,192 +0,0 @@ -# coding=UTF-8 -''' -Copyright (c) 2010 openpyxl - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - -@license: http://www.opensource.org/licenses/mit-license.php -@author: Eric Gazoni -''' - -from ..shared.xmltools import Element, SubElement, get_document_content - - -class DrawingWriter(object): - """ one main drawing file per sheet """ - - def __init__(self, sheet): - self._sheet = sheet - - def write(self): - """ write drawings for one sheet in one file """ - - root = Element('xdr:wsDr', - {'xmlns:xdr' : "http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing", - 'xmlns:a' : "http://schemas.openxmlformats.org/drawingml/2006/main"}) - - for i, chart in enumerate(self._sheet._charts): - - drawing = chart.drawing - -# anchor = SubElement(root, 'xdr:twoCellAnchor') -# (start_row, start_col), (end_row, end_col) = drawing.coordinates -# # anchor coordinates -# _from = SubElement(anchor, 'xdr:from') -# x = SubElement(_from, 'xdr:col').text = str(start_col) -# x = SubElement(_from, 'xdr:colOff').text = '0' -# x = SubElement(_from, 'xdr:row').text = str(start_row) -# x = SubElement(_from, 'xdr:rowOff').text = '0' - -# _to = SubElement(anchor, 'xdr:to') -# x = SubElement(_to, 'xdr:col').text = str(end_col) -# x = SubElement(_to, 'xdr:colOff').text = '0' -# x = SubElement(_to, 'xdr:row').text = str(end_row) -# x = SubElement(_to, 'xdr:rowOff').text = '0' - - # we only support absolute anchor atm (TODO: oneCellAnchor, twoCellAnchor - x, y, w, h = drawing.get_emu_dimensions() - anchor = SubElement(root, 'xdr:absoluteAnchor') - SubElement(anchor, 'xdr:pos', {'x':str(x), 'y':str(y)}) - SubElement(anchor, 'xdr:ext', {'cx':str(w), 'cy':str(h)}) - - # graph frame - frame = SubElement(anchor, 'xdr:graphicFrame', {'macro':''}) - - name = SubElement(frame, 'xdr:nvGraphicFramePr') - SubElement(name, 'xdr:cNvPr', {'id':'%s' % i, 'name':'Graphique %s' % i}) - SubElement(name, 'xdr:cNvGraphicFramePr') - - frm = SubElement(frame, 'xdr:xfrm') - # no transformation - SubElement(frm, 'a:off', {'x':'0', 'y':'0'}) - SubElement(frm, 'a:ext', {'cx':'0', 'cy':'0'}) - - graph = SubElement(frame, 'a:graphic') - data = SubElement(graph, 'a:graphicData', - {'uri':'http://schemas.openxmlformats.org/drawingml/2006/chart'}) - SubElement(data, 'c:chart', - { 'xmlns:c':'http://schemas.openxmlformats.org/drawingml/2006/chart', - 'xmlns:r':'http://schemas.openxmlformats.org/officeDocument/2006/relationships', - 'r:id':'rId%s' % (i + 1)}) - - SubElement(anchor, 'xdr:clientData') - - return get_document_content(root) - - def write_rels(self, chart_id): - - root = Element('Relationships', - {'xmlns' : 'http://schemas.openxmlformats.org/package/2006/relationships'}) - for i, chart in enumerate(self._sheet._charts): - attrs = {'Id' : 'rId%s' % (i + 1), - 'Type' : 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart', - 'Target' : '../charts/chart%s.xml' % (chart_id + i) } - SubElement(root, 'Relationship', attrs) - return get_document_content(root) - -class ShapeWriter(object): - """ one file per shape """ - - schema = "http://schemas.openxmlformats.org/drawingml/2006/main" - - def __init__(self, shapes): - - self._shapes = shapes - - def write(self, shape_id): - - root = Element('c:userShapes', {'xmlns:c' : 'http://schemas.openxmlformats.org/drawingml/2006/chart'}) - - for shape in self._shapes: - anchor = SubElement(root, 'cdr:relSizeAnchor', - {'xmlns:cdr' : "http://schemas.openxmlformats.org/drawingml/2006/chartDrawing"}) - - xstart, ystart, xend, yend = shape.get_coordinates() - - _from = SubElement(anchor, 'cdr:from') - SubElement(_from, 'cdr:x').text = str(xstart) - SubElement(_from, 'cdr:y').text = str(ystart) - - _to = SubElement(anchor, 'cdr:to') - SubElement(_to, 'cdr:x').text = str(xend) - SubElement(_to, 'cdr:y').text = str(yend) - - sp = SubElement(anchor, 'cdr:sp', {'macro':'', 'textlink':''}) - nvspr = SubElement(sp, 'cdr:nvSpPr') - SubElement(nvspr, 'cdr:cNvPr', {'id':str(shape_id), 'name':'shape %s' % shape_id}) - SubElement(nvspr, 'cdr:cNvSpPr') - - sppr = SubElement(sp, 'cdr:spPr') - frm = SubElement(sppr, 'a:xfrm', {'xmlns:a':self.schema}) - # no transformation - SubElement(frm, 'a:off', {'x':'0', 'y':'0'}) - SubElement(frm, 'a:ext', {'cx':'0', 'cy':'0'}) - - prstgeom = SubElement(sppr, 'a:prstGeom', {'xmlns:a':self.schema, 'prst':str(shape.style)}) - SubElement(prstgeom, 'a:avLst') - - fill = SubElement(sppr, 'a:solidFill', {'xmlns:a':self.schema}) - SubElement(fill, 'a:srgbClr', {'val':shape.color}) - - border = SubElement(sppr, 'a:ln', {'xmlns:a':self.schema, 'w':str(shape._border_width)}) - sf = SubElement(border, 'a:solidFill') - SubElement(sf, 'a:srgbClr', {'val':shape.border_color}) - - self._write_style(sp) - self._write_text(sp, shape) - - shape_id += 1 - - return get_document_content(root) - - def _write_text(self, node, shape): - """ write text in the shape """ - - tx_body = SubElement(node, 'cdr:txBody') - SubElement(tx_body, 'a:bodyPr', {'xmlns:a':self.schema, 'vertOverflow':'clip'}) - SubElement(tx_body, 'a:lstStyle', - {'xmlns:a':self.schema}) - p = SubElement(tx_body, 'a:p', {'xmlns:a':self.schema}) - if shape.text: - r = SubElement(p, 'a:r') - rpr = SubElement(r, 'a:rPr', {'lang':'en-US'}) - fill = SubElement(rpr, 'a:solidFill') - SubElement(fill, 'a:srgbClr', {'val':shape.text_color}) - - SubElement(r, 'a:t').text = shape.text - else: - SubElement(p, 'a:endParaRPr', {'lang':'en-US'}) - - def _write_style(self, node): - """ write style theme """ - - style = SubElement(node, 'cdr:style') - - ln_ref = SubElement(style, 'a:lnRef', {'xmlns:a':self.schema, 'idx':'2'}) - scheme_clr = SubElement(ln_ref, 'a:schemeClr', {'val':'accent1'}) - SubElement(scheme_clr, 'a:shade', {'val':'50000'}) - - fill_ref = SubElement(style, 'a:fillRef', {'xmlns:a':self.schema, 'idx':'1'}) - SubElement(fill_ref, 'a:schemeClr', {'val':'accent1'}) - - effect_ref = SubElement(style, 'a:effectRef', {'xmlns:a':self.schema, 'idx':'0'}) - SubElement(effect_ref, 'a:schemeClr', {'val':'accent1'}) - - font_ref = SubElement(style, 'a:fontRef', {'xmlns:a':self.schema, 'idx':'minor'}) - SubElement(font_ref, 'a:schemeClr', {'val':'lt1'}) diff --git a/tablib/packages/openpyxl3/writer/dump_worksheet.py b/tablib/packages/openpyxl3/writer/dump_worksheet.py deleted file mode 100644 index 36d68d4e..00000000 --- a/tablib/packages/openpyxl3/writer/dump_worksheet.py +++ /dev/null @@ -1,256 +0,0 @@ -# file openpyxl/writer/straight_worksheet.py - -# Copyright (c) 2010 openpyxl -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -# @license: http://www.opensource.org/licenses/mit-license.php -# @author: Eric Gazoni - -"""Write worksheets to xml representations in an optimized way""" - -import datetime -import os - -from ..cell import column_index_from_string, get_column_letter, Cell -from ..worksheet import Worksheet -from ..shared.xmltools import XMLGenerator, get_document_content, \ - start_tag, end_tag, tag -from ..shared.date_time import SharedDate -from ..shared.ooxml import MAX_COLUMN, MAX_ROW -from tempfile import NamedTemporaryFile -from ..writer.excel import ExcelWriter -from ..writer.strings import write_string_table -from ..writer.styles import StyleWriter -from ..style import Style, NumberFormat - -from ..shared.ooxml import ARC_SHARED_STRINGS, ARC_CONTENT_TYPES, \ - ARC_ROOT_RELS, ARC_WORKBOOK_RELS, ARC_APP, ARC_CORE, ARC_THEME, \ - ARC_STYLE, ARC_WORKBOOK, \ - PACKAGE_WORKSHEETS, PACKAGE_DRAWINGS, PACKAGE_CHARTS - -STYLES = {'datetime' : {'type':Cell.TYPE_NUMERIC, - 'style':'1'}, - 'string':{'type':Cell.TYPE_STRING, - 'style':'0'}, - 'numeric':{'type':Cell.TYPE_NUMERIC, - 'style':'0'}, - 'formula':{'type':Cell.TYPE_FORMULA, - 'style':'0'}, - 'boolean':{'type':Cell.TYPE_BOOL, - 'style':'0'}, - } - -DATETIME_STYLE = Style() -DATETIME_STYLE.number_format.format_code = NumberFormat.FORMAT_DATE_YYYYMMDD2 -BOUNDING_BOX_PLACEHOLDER = 'A1:%s%d' % (get_column_letter(MAX_COLUMN), MAX_ROW) - -class DumpWorksheet(Worksheet): - - """ - .. warning:: - - You shouldn't initialize this yourself, use :class:`..workbook.Workbook` constructor instead, - with `optimized_write = True`. - """ - - def __init__(self, parent_workbook): - - Worksheet.__init__(self, parent_workbook) - - self._max_col = 0 - self._max_row = 0 - self._parent = parent_workbook - self._fileobj_header = NamedTemporaryFile(mode='r+', prefix='..', suffix='.header', delete=False) - self._fileobj_content = NamedTemporaryFile(mode='r+', prefix='..', suffix='.content', delete=False) - self._fileobj = NamedTemporaryFile(mode='w', prefix='..', delete=False) - self.doc = XMLGenerator(self._fileobj_content, 'utf-8') - self.header = XMLGenerator(self._fileobj_header, 'utf-8') - self.title = 'Sheet' - - self._shared_date = SharedDate() - self._string_builder = self._parent.strings_table_builder - - @property - def filename(self): - return self._fileobj.name - - def write_header(self): - - doc = self.header - - start_tag(doc, 'worksheet', - {'xml:space': 'preserve', - 'xmlns': 'http://schemas.openxmlformats.org/spreadsheetml/2006/main', - 'xmlns:r': 'http://schemas.openxmlformats.org/officeDocument/2006/relationships'}) - start_tag(doc, 'sheetPr') - tag(doc, 'outlinePr', - {'summaryBelow': '1', - 'summaryRight': '1'}) - end_tag(doc, 'sheetPr') - tag(doc, 'dimension', {'ref': 'A1:%s' % (self.get_dimensions())}) - start_tag(doc, 'sheetViews') - start_tag(doc, 'sheetView', {'workbookViewId': '0'}) - tag(doc, 'selection', {'activeCell': 'A1', - 'sqref': 'A1'}) - end_tag(doc, 'sheetView') - end_tag(doc, 'sheetViews') - tag(doc, 'sheetFormatPr', {'defaultRowHeight': '15'}) - start_tag(doc, 'sheetData') - - def close(self): - - self._close_content() - self._close_header() - - self._write_fileobj(self._fileobj_header) - self._write_fileobj(self._fileobj_content) - - self._fileobj.close() - - def _write_fileobj(self, fobj): - - fobj.flush() - fobj.seek(0) - - while True: - chunk = fobj.read(4096) - if not chunk: - break - self._fileobj.write(chunk) - - fobj.close() - os.remove(fobj.name) - - self._fileobj.flush() - - def _close_header(self): - - doc = self.header - #doc.endDocument() - - def _close_content(self): - - doc = self.doc - end_tag(doc, 'sheetData') - - end_tag(doc, 'worksheet') - #doc.endDocument() - - def get_dimensions(self): - - if not self._max_col or not self._max_row: - return 'A1' - else: - return '%s%d' % (get_column_letter(self._max_col), (self._max_row)) - - def append(self, row): - - """ - :param row: iterable containing values to append - :type row: iterable - """ - - doc = self.doc - - self._max_row += 1 - span = len(row) - self._max_col = max(self._max_col, span) - - row_idx = self._max_row - - attrs = {'r': '%d' % row_idx, - 'spans': '1:%d' % span} - - start_tag(doc, 'row', attrs) - - for col_idx, cell in enumerate(row): - - if cell is None: - continue - - coordinate = '%s%d' % (get_column_letter(col_idx+1), row_idx) - attributes = {'r': coordinate} - - if isinstance(cell, bool): - dtype = 'boolean' - elif isinstance(cell, (int, float)): - dtype = 'numeric' - elif isinstance(cell, (datetime.datetime, datetime.date)): - dtype = 'datetime' - cell = self._shared_date.datetime_to_julian(cell) - attributes['s'] = STYLES[dtype]['style'] - elif cell and cell[0] == '=': - dtype = 'formula' - else: - dtype = 'string' - cell = self._string_builder.add(cell) - - attributes['t'] = STYLES[dtype]['type'] - - start_tag(doc, 'c', attributes) - - if dtype == 'formula': - tag(doc, 'f', body = '%s' % cell[1:]) - tag(doc, 'v') - else: - tag(doc, 'v', body = '%s' % cell) - - end_tag(doc, 'c') - - - end_tag(doc, 'row') - - -def save_dump(workbook, filename): - - writer = ExcelDumpWriter(workbook) - writer.save(filename) - return True - -class ExcelDumpWriter(ExcelWriter): - - def __init__(self, workbook): - - self.workbook = workbook - self.style_writer = StyleDumpWriter(workbook) - self.style_writer._style_list.append(DATETIME_STYLE) - - def _write_string_table(self, archive): - - shared_string_table = self.workbook.strings_table_builder.get_table() - archive.writestr(ARC_SHARED_STRINGS, - write_string_table(shared_string_table)) - - return shared_string_table - - def _write_worksheets(self, archive, shared_string_table, style_writer): - - for i, sheet in enumerate(self.workbook.worksheets): - sheet.write_header() - sheet.close() - archive.write(sheet.filename, PACKAGE_WORKSHEETS + '/sheet%d.xml' % (i + 1)) - os.remove(sheet.filename) - - -class StyleDumpWriter(StyleWriter): - - def _get_style_list(self, workbook): - return [] - diff --git a/tablib/packages/openpyxl3/writer/excel.py b/tablib/packages/openpyxl3/writer/excel.py deleted file mode 100644 index a19666d0..00000000 --- a/tablib/packages/openpyxl3/writer/excel.py +++ /dev/null @@ -1,156 +0,0 @@ -# file openpyxl/writer/excel.py - -# Copyright (c) 2010 openpyxl -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -# @license: http://www.opensource.org/licenses/mit-license.php -# @author: Eric Gazoni - -"""Write a .xlsx file.""" - -# Python stdlib imports -from zipfile import ZipFile, ZIP_DEFLATED -from io import StringIO - -# package imports -from ..shared.ooxml import ARC_SHARED_STRINGS, ARC_CONTENT_TYPES, \ - ARC_ROOT_RELS, ARC_WORKBOOK_RELS, ARC_APP, ARC_CORE, ARC_THEME, \ - ARC_STYLE, ARC_WORKBOOK, \ - PACKAGE_WORKSHEETS, PACKAGE_DRAWINGS, PACKAGE_CHARTS -from .strings import create_string_table, write_string_table -from .workbook import write_content_types, write_root_rels, \ - write_workbook_rels, write_properties_app, write_properties_core, \ - write_workbook -from .theme import write_theme -from .styles import StyleWriter -from .drawings import DrawingWriter, ShapeWriter -from .charts import ChartWriter -from .worksheet import write_worksheet, write_worksheet_rels - - -class ExcelWriter(object): - """Write a workbook object to an Excel file.""" - - def __init__(self, workbook): - self.workbook = workbook - self.style_writer = StyleWriter(self.workbook) - - def write_data(self, archive): - """Write the various xml files into the zip archive.""" - # cleanup all worksheets - shared_string_table = self._write_string_table(archive) - - archive.writestr(ARC_CONTENT_TYPES, write_content_types(self.workbook)) - archive.writestr(ARC_ROOT_RELS, write_root_rels(self.workbook)) - archive.writestr(ARC_WORKBOOK_RELS, write_workbook_rels(self.workbook)) - archive.writestr(ARC_APP, write_properties_app(self.workbook)) - archive.writestr(ARC_CORE, - write_properties_core(self.workbook.properties)) - archive.writestr(ARC_THEME, write_theme()) - archive.writestr(ARC_STYLE, self.style_writer.write_table()) - archive.writestr(ARC_WORKBOOK, write_workbook(self.workbook)) - - self._write_worksheets(archive, shared_string_table, self.style_writer) - - def _write_string_table(self, archive): - - for ws in self.workbook.worksheets: - ws.garbage_collect() - shared_string_table = create_string_table(self.workbook) - archive.writestr(ARC_SHARED_STRINGS, - write_string_table(shared_string_table)) - - return shared_string_table - - def _write_worksheets(self, archive, shared_string_table, style_writer): - - drawing_id = 1 - chart_id = 1 - shape_id = 1 - - for i, sheet in enumerate(self.workbook.worksheets): - archive.writestr(PACKAGE_WORKSHEETS + '/sheet%d.xml' % (i + 1), - write_worksheet(sheet, shared_string_table, - style_writer.get_style_by_hash())) - if sheet._charts or sheet.relationships: - archive.writestr(PACKAGE_WORKSHEETS + - '/_rels/sheet%d.xml.rels' % (i + 1), - write_worksheet_rels(sheet, drawing_id)) - if sheet._charts: - dw = DrawingWriter(sheet) - archive.writestr(PACKAGE_DRAWINGS + '/drawing%d.xml' % drawing_id, - dw.write()) - archive.writestr(PACKAGE_DRAWINGS + '/_rels/drawing%d.xml.rels' % drawing_id, - dw.write_rels(chart_id)) - drawing_id += 1 - - for chart in sheet._charts: - cw = ChartWriter(chart) - archive.writestr(PACKAGE_CHARTS + '/chart%d.xml' % chart_id, - cw.write()) - - if chart._shapes: - archive.writestr(PACKAGE_CHARTS + '/_rels/chart%d.xml.rels' % chart_id, - cw.write_rels(drawing_id)) - sw = ShapeWriter(chart._shapes) - archive.writestr(PACKAGE_DRAWINGS + '/drawing%d.xml' % drawing_id, - sw.write(shape_id)) - shape_id += len(chart._shapes) - drawing_id += 1 - - chart_id += 1 - - - def save(self, filename): - """Write data into the archive.""" - archive = ZipFile(filename, 'w', ZIP_DEFLATED) - self.write_data(archive) - archive.close() - - -def save_workbook(workbook, filename): - """Save the given workbook on the filesystem under the name filename. - - :param workbook: the workbook to save - :type workbook: :class:`openpyxl.workbook.Workbook` - - :param filename: the path to which save the workbook - :type filename: string - - :rtype: bool - - """ - writer = ExcelWriter(workbook) - writer.save(filename) - return True - - -def save_virtual_workbook(workbook): - """Return an in-memory workbook, suitable for a Django response.""" - writer = ExcelWriter(workbook) - temp_buffer = StringIO() - try: - archive = ZipFile(temp_buffer, 'w', ZIP_DEFLATED) - writer.write_data(archive) - finally: - archive.close() - virtual_workbook = temp_buffer.getvalue() - temp_buffer.close() - return virtual_workbook diff --git a/tablib/packages/openpyxl3/writer/strings.py b/tablib/packages/openpyxl3/writer/strings.py deleted file mode 100644 index 706c2b64..00000000 --- a/tablib/packages/openpyxl3/writer/strings.py +++ /dev/null @@ -1,86 +0,0 @@ -# file openpyxl/writer/strings.py - -# Copyright (c) 2010 openpyxl -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -# @license: http://www.opensource.org/licenses/mit-license.php -# @author: Eric Gazoni - -"""Write the shared string table.""" - -# Python stdlib imports -from io import StringIO - -# package imports -from ..shared.xmltools import start_tag, end_tag, tag, XMLGenerator - - -def create_string_table(workbook): - """Compile the string table for a workbook.""" - strings = set() - for sheet in workbook.worksheets: - for cell in sheet.get_cell_collection(): - if cell.data_type == cell.TYPE_STRING and cell._value is not None: - strings.add(cell.value) - return dict((key, i) for i, key in enumerate(strings)) - - -def write_string_table(string_table): - """Write the string table xml.""" - temp_buffer = StringIO() - doc = XMLGenerator(temp_buffer, 'utf-8') - start_tag(doc, 'sst', {'xmlns': - 'http://schemas.openxmlformats.org/spreadsheetml/2006/main', - 'uniqueCount': '%d' % len(string_table)}) - strings_to_write = sorted(iter(string_table.items()), - key=lambda pair: pair[1]) - for key in [pair[0] for pair in strings_to_write]: - start_tag(doc, 'si') - if key.strip() != key: - attr = {'xml:space': 'preserve'} - else: - attr = {} - tag(doc, 't', attr, key) - end_tag(doc, 'si') - end_tag(doc, 'sst') - string_table_xml = temp_buffer.getvalue() - temp_buffer.close() - return string_table_xml - -class StringTableBuilder(object): - - def __init__(self): - - self.counter = 0 - self.dct = {} - - def add(self, key): - - key = key.strip() - try: - return self.dct[key] - except KeyError: - res = self.dct[key] = self.counter - self.counter += 1 - return res - - def get_table(self): - - return self.dct diff --git a/tablib/packages/openpyxl3/writer/styles.py b/tablib/packages/openpyxl3/writer/styles.py deleted file mode 100644 index 3d73382f..00000000 --- a/tablib/packages/openpyxl3/writer/styles.py +++ /dev/null @@ -1,256 +0,0 @@ -# file openpyxl/writer/styles.py - -# Copyright (c) 2010 openpyxl -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -# @license: http://www.opensource.org/licenses/mit-license.php -# @author: Eric Gazoni - -"""Write the shared style table.""" - -# package imports -from ..shared.xmltools import Element, SubElement -from ..shared.xmltools import get_document_content -from .. import style - -class StyleWriter(object): - - def __init__(self, workbook): - self._style_list = self._get_style_list(workbook) - self._root = Element('styleSheet', - {'xmlns':'http://schemas.openxmlformats.org/spreadsheetml/2006/main'}) - - def _get_style_list(self, workbook): - crc = {} - for worksheet in workbook.worksheets: - for style in list(worksheet._styles.values()): - crc[hash(style)] = style - self.style_table = dict([(style, i+1) \ - for i, style in enumerate(list(crc.values()))]) - sorted_styles = sorted(iter(self.style_table.items()), \ - key = lambda pair:pair[1]) - return [s[0] for s in sorted_styles] - - def get_style_by_hash(self): - return dict([(hash(style), id) \ - for style, id in self.style_table.items()]) - - def write_table(self): - number_format_table = self._write_number_formats() - fonts_table = self._write_fonts() - fills_table = self._write_fills() - borders_table = self._write_borders() - self._write_cell_style_xfs() - self._write_cell_xfs(number_format_table, fonts_table, fills_table, borders_table) - self._write_cell_style() - self._write_dxfs() - self._write_table_styles() - - return get_document_content(xml_node=self._root) - - def _write_fonts(self): - """ add fonts part to root - return {font.crc => index} - """ - - fonts = SubElement(self._root, 'fonts') - - # default - font_node = SubElement(fonts, 'font') - SubElement(font_node, 'sz', {'val':'11'}) - SubElement(font_node, 'color', {'theme':'1'}) - SubElement(font_node, 'name', {'val':'Calibri'}) - SubElement(font_node, 'family', {'val':'2'}) - SubElement(font_node, 'scheme', {'val':'minor'}) - - # others - table = {} - index = 1 - for st in self._style_list: - if hash(st.font) != hash(style.DEFAULTS.font) and hash(st.font) not in table: - table[hash(st.font)] = str(index) - font_node = SubElement(fonts, 'font') - SubElement(font_node, 'sz', {'val':str(st.font.size)}) - SubElement(font_node, 'color', {'rgb':str(st.font.color.index)}) - SubElement(font_node, 'name', {'val':st.font.name}) - SubElement(font_node, 'family', {'val':'2'}) - SubElement(font_node, 'scheme', {'val':'minor'}) - if st.font.bold: - SubElement(font_node, 'b') - if st.font.italic: - SubElement(font_node, 'i') - index += 1 - - fonts.attrib["count"] = str(index) - return table - - def _write_fills(self): - fills = SubElement(self._root, 'fills', {'count':'2'}) - fill = SubElement(fills, 'fill') - SubElement(fill, 'patternFill', {'patternType':'none'}) - fill = SubElement(fills, 'fill') - SubElement(fill, 'patternFill', {'patternType':'gray125'}) - - table = {} - index = 2 - for st in self._style_list: - if hash(st.fill) != hash(style.DEFAULTS.fill) and hash(st.fill) not in table: - table[hash(st.fill)] = str(index) - fill = SubElement(fills, 'fill') - if hash(st.fill.fill_type) != hash(style.DEFAULTS.fill.fill_type): - node = SubElement(fill,'patternFill', {'patternType':st.fill.fill_type}) - if hash(st.fill.start_color) != hash(style.DEFAULTS.fill.start_color): - - SubElement(node, 'fgColor', {'rgb':str(st.fill.start_color.index)}) - if hash(st.fill.end_color) != hash(style.DEFAULTS.fill.end_color): - SubElement(node, 'bgColor', {'rgb':str(st.fill.start_color.index)}) - index += 1 - - fills.attrib["count"] = str(index) - return table - - def _write_borders(self): - borders = SubElement(self._root, 'borders') - - # default - border = SubElement(borders, 'border') - SubElement(border, 'left') - SubElement(border, 'right') - SubElement(border, 'top') - SubElement(border, 'bottom') - SubElement(border, 'diagonal') - - # others - table = {} - index = 1 - for st in self._style_list: - if hash(st.borders) != hash(style.DEFAULTS.borders) and hash(st.borders) not in table: - table[hash(st.borders)] = str(index) - border = SubElement(borders, 'border') - # caution: respect this order - for side in ('left','right','top','bottom','diagonal'): - obj = getattr(st.borders, side) - node = SubElement(border, side, {'style':obj.border_style}) - SubElement(node, 'color', {'rgb':str(obj.color.index)}) - index += 1 - - borders.attrib["count"] = str(index) - return table - - def _write_cell_style_xfs(self): - cell_style_xfs = SubElement(self._root, 'cellStyleXfs', {'count':'1'}) - xf = SubElement(cell_style_xfs, 'xf', - {'numFmtId':"0", 'fontId':"0", 'fillId':"0", 'borderId':"0"}) - - def _write_cell_xfs(self, number_format_table, fonts_table, fills_table, borders_table): - """ write styles combinations based on ids found in tables """ - - # writing the cellXfs - cell_xfs = SubElement(self._root, 'cellXfs', - {'count':'%d' % (len(self._style_list) + 1)}) - - # default - def _get_default_vals(): - return dict(numFmtId='0', fontId='0', fillId='0', - xfId='0', borderId='0') - - SubElement(cell_xfs, 'xf', _get_default_vals()) - - for st in self._style_list: - vals = _get_default_vals() - - if hash(st.font) != hash(style.DEFAULTS.font): - vals['fontId'] = fonts_table[hash(st.font)] - vals['applyFont'] = '1' - - if hash(st.borders) != hash(style.DEFAULTS.borders): - vals['borderId'] = borders_table[hash(st.borders)] - vals['applyBorder'] = '1' - - if hash(st.fill) != hash(style.DEFAULTS.fill): - vals['fillId'] = fills_table[hash(st.fill)] - vals['applyFillId'] = '1' - - if st.number_format != style.DEFAULTS.number_format: - vals['numFmtId'] = '%d' % number_format_table[st.number_format] - vals['applyNumberFormat'] = '1' - - if hash(st.alignment) != hash(style.DEFAULTS.alignment): - vals['applyAlignment'] = '1' - - node = SubElement(cell_xfs, 'xf', vals) - - if hash(st.alignment) != hash(style.DEFAULTS.alignment): - alignments = {} - - for align_attr in ['horizontal','vertical']: - if hash(getattr(st.alignment, align_attr)) != hash(getattr(style.DEFAULTS.alignment, align_attr)): - alignments[align_attr] = getattr(st.alignment, align_attr) - - SubElement(node, 'alignment', alignments) - - - def _write_cell_style(self): - cell_styles = SubElement(self._root, 'cellStyles', {'count':'1'}) - cell_style = SubElement(cell_styles, 'cellStyle', - {'name':"Normal", 'xfId':"0", 'builtinId':"0"}) - - def _write_dxfs(self): - dxfs = SubElement(self._root, 'dxfs', {'count':'0'}) - - def _write_table_styles(self): - - table_styles = SubElement(self._root, 'tableStyles', - {'count':'0', 'defaultTableStyle':'TableStyleMedium9', - 'defaultPivotStyle':'PivotStyleLight16'}) - - def _write_number_formats(self): - - number_format_table = {} - - number_format_list = [] - exceptions_list = [] - num_fmt_id = 165 # start at a greatly higher value as any builtin can go - num_fmt_offset = 0 - - for style in self._style_list: - - if not style.number_format in number_format_list : - number_format_list.append(style.number_format) - - for number_format in number_format_list: - - if number_format.is_builtin(): - btin = number_format.builtin_format_id(number_format.format_code) - number_format_table[number_format] = btin - else: - number_format_table[number_format] = num_fmt_id + num_fmt_offset - num_fmt_offset += 1 - exceptions_list.append(number_format) - - num_fmts = SubElement(self._root, 'numFmts', - {'count':'%d' % len(exceptions_list)}) - - for number_format in exceptions_list : - SubElement(num_fmts, 'numFmt', - {'numFmtId':'%d' % number_format_table[number_format], - 'formatCode':'%s' % number_format.format_code}) - - return number_format_table diff --git a/tablib/packages/openpyxl3/writer/theme.py b/tablib/packages/openpyxl3/writer/theme.py deleted file mode 100644 index 80700f2c..00000000 --- a/tablib/packages/openpyxl3/writer/theme.py +++ /dev/null @@ -1,202 +0,0 @@ -# -*- coding: utf-8 -*- -# file openpyxl/writer/theme.py - -# Copyright (c) 2010 openpyxl -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -# @license: http://www.opensource.org/licenses/mit-license.php -# @author: Eric Gazoni - -"""Write the theme xml based on a fixed string.""" - -# package imports -from ..shared.xmltools import fromstring, get_document_content - - -def write_theme(): - """Write the theme xml.""" - xml_node = fromstring( - '\n' - - '' - '' - - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '' - '') - return get_document_content(xml_node) diff --git a/tablib/packages/openpyxl3/writer/workbook.py b/tablib/packages/openpyxl3/writer/workbook.py deleted file mode 100644 index e7b390c1..00000000 --- a/tablib/packages/openpyxl3/writer/workbook.py +++ /dev/null @@ -1,204 +0,0 @@ -# file openpyxl/writer/workbook.py - -# Copyright (c) 2010 openpyxl -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -# @license: http://www.opensource.org/licenses/mit-license.php -# @author: Eric Gazoni - -"""Write the workbook global settings to the archive.""" - -# package imports -from ..shared.xmltools import Element, SubElement -from ..cell import absolute_coordinate -from ..shared.xmltools import get_document_content -from ..shared.ooxml import NAMESPACES, ARC_CORE, ARC_WORKBOOK, \ - ARC_APP, ARC_THEME, ARC_STYLE, ARC_SHARED_STRINGS -from ..shared.date_time import datetime_to_W3CDTF - - -def write_properties_core(properties): - """Write the core properties to xml.""" - root = Element('cp:coreProperties', {'xmlns:cp': NAMESPACES['cp'], - 'xmlns:xsi': NAMESPACES['xsi'], 'xmlns:dc': NAMESPACES['dc'], - 'xmlns:dcterms': NAMESPACES['dcterms'], - 'xmlns:dcmitype': NAMESPACES['dcmitype'], }) - SubElement(root, 'dc:creator').text = properties.creator - SubElement(root, 'cp:lastModifiedBy').text = properties.last_modified_by - SubElement(root, 'dcterms:created', \ - {'xsi:type': 'dcterms:W3CDTF'}).text = \ - datetime_to_W3CDTF(properties.created) - SubElement(root, 'dcterms:modified', - {'xsi:type': 'dcterms:W3CDTF'}).text = \ - datetime_to_W3CDTF(properties.modified) - return get_document_content(root) - - -def write_content_types(workbook): - """Write the content-types xml.""" - root = Element('Types', {'xmlns': 'http://schemas.openxmlformats.org/package/2006/content-types'}) - SubElement(root, 'Override', {'PartName': '/' + ARC_THEME, 'ContentType': 'application/vnd.openxmlformats-officedocument.theme+xml'}) - SubElement(root, 'Override', {'PartName': '/' + ARC_STYLE, 'ContentType': 'application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml'}) - SubElement(root, 'Default', {'Extension': 'rels', 'ContentType': 'application/vnd.openxmlformats-package.relationships+xml'}) - SubElement(root, 'Default', {'Extension': 'xml', 'ContentType': 'application/xml'}) - SubElement(root, 'Override', {'PartName': '/' + ARC_WORKBOOK, 'ContentType': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml'}) - SubElement(root, 'Override', {'PartName': '/' + ARC_APP, 'ContentType': 'application/vnd.openxmlformats-officedocument.extended-properties+xml'}) - SubElement(root, 'Override', {'PartName': '/' + ARC_CORE, 'ContentType': 'application/vnd.openxmlformats-package.core-properties+xml'}) - SubElement(root, 'Override', {'PartName': '/' + ARC_SHARED_STRINGS, 'ContentType': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml'}) - - drawing_id = 1 - chart_id = 1 - - for sheet_id, sheet in enumerate(workbook.worksheets): - SubElement(root, 'Override', - {'PartName': '/xl/worksheets/sheet%d.xml' % (sheet_id + 1), - 'ContentType': 'application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml'}) - if sheet._charts: - SubElement(root, 'Override', - {'PartName' : '/xl/drawings/drawing%d.xml' % (sheet_id + 1), - 'ContentType' : 'application/vnd.openxmlformats-officedocument.drawing+xml'}) - drawing_id += 1 - - for chart in sheet._charts: - SubElement(root, 'Override', - {'PartName' : '/xl/charts/chart%d.xml' % chart_id, - 'ContentType' : 'application/vnd.openxmlformats-officedocument.drawingml.chart+xml'}) - chart_id += 1 - if chart._shapes: - SubElement(root, 'Override', - {'PartName' : '/xl/drawings/drawing%d.xml' % drawing_id, - 'ContentType' : 'application/vnd.openxmlformats-officedocument.drawingml.chartshapes+xml'}) - drawing_id += 1 - - return get_document_content(root) - - -def write_properties_app(workbook): - """Write the properties xml.""" - worksheets_count = len(workbook.worksheets) - root = Element('Properties', {'xmlns': 'http://schemas.openxmlformats.org/officeDocument/2006/extended-properties', - 'xmlns:vt': 'http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes'}) - SubElement(root, 'Application').text = 'Microsoft Excel' - SubElement(root, 'DocSecurity').text = '0' - SubElement(root, 'ScaleCrop').text = 'false' - SubElement(root, 'Company') - SubElement(root, 'LinksUpToDate').text = 'false' - SubElement(root, 'SharedDoc').text = 'false' - SubElement(root, 'HyperlinksChanged').text = 'false' - SubElement(root, 'AppVersion').text = '12.0000' - - # heading pairs part - heading_pairs = SubElement(root, 'HeadingPairs') - vector = SubElement(heading_pairs, 'vt:vector', - {'size': '2', 'baseType': 'variant'}) - variant = SubElement(vector, 'vt:variant') - SubElement(variant, 'vt:lpstr').text = 'Worksheets' - variant = SubElement(vector, 'vt:variant') - SubElement(variant, 'vt:i4').text = '%d' % worksheets_count - - # title of parts - title_of_parts = SubElement(root, 'TitlesOfParts') - vector = SubElement(title_of_parts, 'vt:vector', - {'size': '%d' % worksheets_count, 'baseType': 'lpstr'}) - for ws in workbook.worksheets: - SubElement(vector, 'vt:lpstr').text = '%s' % ws.title - return get_document_content(root) - - -def write_root_rels(workbook): - """Write the relationships xml.""" - root = Element('Relationships', {'xmlns': - 'http://schemas.openxmlformats.org/package/2006/relationships'}) - SubElement(root, 'Relationship', {'Id': 'rId1', 'Target': ARC_WORKBOOK, - 'Type': 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument'}) - SubElement(root, 'Relationship', {'Id': 'rId2', 'Target': ARC_CORE, - 'Type': 'http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties'}) - SubElement(root, 'Relationship', {'Id': 'rId3', 'Target': ARC_APP, - 'Type': 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties'}) - return get_document_content(root) - - -def write_workbook(workbook): - """Write the core workbook xml.""" - root = Element('workbook', {'xmlns': 'http://schemas.openxmlformats.org/spreadsheetml/2006/main', - 'xml:space': 'preserve', 'xmlns:r': 'http://schemas.openxmlformats.org/officeDocument/2006/relationships'}) - SubElement(root, 'fileVersion', {'appName': 'xl', 'lastEdited': '4', - 'lowestEdited': '4', 'rupBuild': '4505'}) - SubElement(root, 'workbookPr', {'defaultThemeVersion': '124226', - 'codeName': 'ThisWorkbook'}) - book_views = SubElement(root, 'bookViews') - SubElement(book_views, 'workbookView', {'activeTab': '%d' % workbook.get_index(workbook.get_active_sheet()), - 'autoFilterDateGrouping': '1', 'firstSheet': '0', 'minimized': '0', - 'showHorizontalScroll': '1', 'showSheetTabs': '1', - 'showVerticalScroll': '1', 'tabRatio': '600', - 'visibility': 'visible'}) - # worksheets - sheets = SubElement(root, 'sheets') - for i, sheet in enumerate(workbook.worksheets): - sheet_node = SubElement(sheets, 'sheet', {'name': sheet.title, - 'sheetId': '%d' % (i + 1), 'r:id': 'rId%d' % (i + 1)}) - if not sheet.sheet_state == sheet.SHEETSTATE_VISIBLE: - sheet_node.set('state', sheet.sheet_state) - # named ranges - defined_names = SubElement(root, 'definedNames') - for named_range in workbook.get_named_ranges(): - name = SubElement(defined_names, 'definedName', - {'name': named_range.name}) - - # as there can be many cells in one range, generate the list of ranges - dest_cells = [] - cell_ids = [] - for worksheet, range_name in named_range.destinations: - cell_ids.append(workbook.get_index(worksheet)) - dest_cells.append("'%s'!%s" % (worksheet.title.replace("'", "''"), - absolute_coordinate(range_name))) - - # for local ranges, we must check all the cells belong to the same sheet - base_id = cell_ids[0] - if named_range.local_only and all([x == base_id for x in cell_ids]): - name.set('localSheetId', '%s' % base_id) - - # finally write the cells list - name.text = ','.join(dest_cells) - - SubElement(root, 'calcPr', {'calcId': '124519', 'calcMode': 'auto', - 'fullCalcOnLoad': '1'}) - return get_document_content(root) - - -def write_workbook_rels(workbook): - """Write the workbook relationships xml.""" - root = Element('Relationships', {'xmlns': - 'http://schemas.openxmlformats.org/package/2006/relationships'}) - for i in range(len(workbook.worksheets)): - SubElement(root, 'Relationship', {'Id': 'rId%d' % (i + 1), - 'Type': 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet', - 'Target': 'worksheets/sheet%s.xml' % (i + 1)}) - rid = len(workbook.worksheets) + 1 - SubElement(root, 'Relationship', - {'Id': 'rId%d' % rid, 'Target': 'sharedStrings.xml', - 'Type': 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings'}) - SubElement(root, 'Relationship', - {'Id': 'rId%d' % (rid + 1), 'Target': 'styles.xml', - 'Type': 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles'}) - SubElement(root, 'Relationship', - {'Id': 'rId%d' % (rid + 2), 'Target': 'theme/theme1.xml', - 'Type': 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme'}) - return get_document_content(root) diff --git a/tablib/packages/openpyxl3/writer/worksheet.py b/tablib/packages/openpyxl3/writer/worksheet.py deleted file mode 100644 index 21d9e9b4..00000000 --- a/tablib/packages/openpyxl3/writer/worksheet.py +++ /dev/null @@ -1,209 +0,0 @@ -# file openpyxl/writer/worksheet.py - -# Copyright (c) 2010 openpyxl -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -# @license: http://www.opensource.org/licenses/mit-license.php -# @author: Eric Gazoni - -"""Write worksheets to xml representations.""" - -# Python stdlib imports -from io import StringIO # cStringIO doesn't handle unicode - -# package imports -from ..cell import coordinate_from_string, column_index_from_string -from ..shared.xmltools import Element, SubElement, XMLGenerator, \ - get_document_content, start_tag, end_tag, tag - - -def row_sort(cell): - """Translate column names for sorting.""" - return column_index_from_string(cell.column) - - -def write_worksheet(worksheet, string_table, style_table): - """Write a worksheet to an xml file.""" - xml_file = StringIO() - doc = XMLGenerator(xml_file, 'utf-8') - start_tag(doc, 'worksheet', - {'xml:space': 'preserve', - 'xmlns': 'http://schemas.openxmlformats.org/spreadsheetml/2006/main', - 'xmlns:r': 'http://schemas.openxmlformats.org/officeDocument/2006/relationships'}) - start_tag(doc, 'sheetPr') - tag(doc, 'outlinePr', - {'summaryBelow': '%d' % (worksheet.show_summary_below), - 'summaryRight': '%d' % (worksheet.show_summary_right)}) - end_tag(doc, 'sheetPr') - tag(doc, 'dimension', {'ref': '%s' % worksheet.calculate_dimension()}) - write_worksheet_sheetviews(doc, worksheet) - tag(doc, 'sheetFormatPr', {'defaultRowHeight': '15'}) - write_worksheet_cols(doc, worksheet) - write_worksheet_data(doc, worksheet, string_table, style_table) - if worksheet.auto_filter: - tag(doc, 'autoFilter', {'ref': worksheet.auto_filter}) - write_worksheet_hyperlinks(doc, worksheet) - if worksheet._charts: - tag(doc, 'drawing', {'r:id':'rId1'}) - end_tag(doc, 'worksheet') - doc.endDocument() - xml_string = xml_file.getvalue() - xml_file.close() - return xml_string - -def write_worksheet_sheetviews(doc, worksheet): - start_tag(doc, 'sheetViews') - start_tag(doc, 'sheetView', {'workbookViewId': '0'}) - selectionAttrs = {} - topLeftCell = worksheet.freeze_panes - if topLeftCell: - colName, row = coordinate_from_string(topLeftCell) - column = column_index_from_string(colName) - pane = 'topRight' - paneAttrs = {} - if column > 1: - paneAttrs['xSplit'] = str(column - 1) - if row > 1: - paneAttrs['ySplit'] = str(row - 1) - pane = 'bottomLeft' - if column > 1: - pane = 'bottomRight' - paneAttrs.update(dict(topLeftCell=topLeftCell, - activePane=pane, - state='frozen')) - tag(doc, 'pane', paneAttrs) - selectionAttrs['pane'] = pane - if row > 1 and column > 1: - tag(doc, 'selection', {'pane': 'topRight'}) - tag(doc, 'selection', {'pane': 'bottomLeft'}) - - selectionAttrs.update({'activeCell': worksheet.active_cell, - 'sqref': worksheet.selected_cell}) - - tag(doc, 'selection', selectionAttrs) - end_tag(doc, 'sheetView') - end_tag(doc, 'sheetViews') - - -def write_worksheet_cols(doc, worksheet): - """Write worksheet columns to xml.""" - if worksheet.column_dimensions: - start_tag(doc, 'cols') - for column_string, columndimension in \ - worksheet.column_dimensions.items(): - col_index = column_index_from_string(column_string) - col_def = {} - col_def['collapsed'] = str(columndimension.style_index) - col_def['min'] = str(col_index) - col_def['max'] = str(col_index) - if columndimension.width != \ - worksheet.default_column_dimension.width: - col_def['customWidth'] = 'true' - if not columndimension.visible: - col_def['hidden'] = 'true' - if columndimension.outline_level > 0: - col_def['outlineLevel'] = str(columndimension.outline_level) - if columndimension.collapsed: - col_def['collapsed'] = 'true' - if columndimension.auto_size: - col_def['bestFit'] = 'true' - if columndimension.width > 0: - col_def['width'] = str(columndimension.width) - else: - col_def['width'] = '9.10' - tag(doc, 'col', col_def) - end_tag(doc, 'cols') - - -def write_worksheet_data(doc, worksheet, string_table, style_table): - """Write worksheet data to xml.""" - start_tag(doc, 'sheetData') - max_column = worksheet.get_highest_column() - style_id_by_hash = style_table - cells_by_row = {} - for cell in worksheet.get_cell_collection(): - cells_by_row.setdefault(cell.row, []).append(cell) - for row_idx in sorted(cells_by_row): - row_dimension = worksheet.row_dimensions[row_idx] - attrs = {'r': '%d' % row_idx, - 'spans': '1:%d' % max_column} - if row_dimension.height > 0: - attrs['ht'] = str(row_dimension.height) - attrs['customHeight'] = '1' - start_tag(doc, 'row', attrs) - row_cells = cells_by_row[row_idx] - sorted_cells = sorted(row_cells, key = row_sort) - for cell in sorted_cells: - value = cell._value - coordinate = cell.get_coordinate() - attributes = {'r': coordinate} - attributes['t'] = cell.data_type - if coordinate in worksheet._styles: - attributes['s'] = '%d' % style_id_by_hash[ - hash(worksheet._styles[coordinate])] - start_tag(doc, 'c', attributes) - if value is None: - tag(doc, 'v', body='') - elif cell.data_type == cell.TYPE_STRING: - tag(doc, 'v', body = '%s' % string_table[value]) - elif cell.data_type == cell.TYPE_FORMULA: - tag(doc, 'f', body = '%s' % value[1:]) - tag(doc, 'v') - elif cell.data_type == cell.TYPE_NUMERIC: - tag(doc, 'v', body = '%s' % value) - else: - tag(doc, 'v', body = '%s' % value) - end_tag(doc, 'c') - end_tag(doc, 'row') - end_tag(doc, 'sheetData') - - -def write_worksheet_hyperlinks(doc, worksheet): - """Write worksheet hyperlinks to xml.""" - write_hyperlinks = False - for cell in worksheet.get_cell_collection(): - if cell.hyperlink_rel_id is not None: - write_hyperlinks = True - break - if write_hyperlinks: - start_tag(doc, 'hyperlinks') - for cell in worksheet.get_cell_collection(): - if cell.hyperlink_rel_id is not None: - attrs = {'display': cell.hyperlink, - 'ref': cell.get_coordinate(), - 'r:id': cell.hyperlink_rel_id} - tag(doc, 'hyperlink', attrs) - end_tag(doc, 'hyperlinks') - - -def write_worksheet_rels(worksheet, idx): - """Write relationships for the worksheet to xml.""" - root = Element('Relationships', {'xmlns': 'http://schemas.openxmlformats.org/package/2006/relationships'}) - for rel in worksheet.relationships: - attrs = {'Id': rel.id, 'Type': rel.type, 'Target': rel.target} - if rel.target_mode: - attrs['TargetMode'] = rel.target_mode - SubElement(root, 'Relationship', attrs) - if worksheet._charts: - attrs = {'Id' : 'rId1', - 'Type' : 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing', - 'Target' : '../drawings/drawing%s.xml' % idx } - SubElement(root, 'Relationship', attrs) - return get_document_content(root) diff --git a/test_tablib.py b/test_tablib.py index 6aa4be43..03a46df1 100755 --- a/test_tablib.py +++ b/test_tablib.py @@ -956,6 +956,11 @@ def test_databook_formatter_support_kwargs(self): """Test XLSX export with formatter configuration.""" self.founders.export('xlsx', freeze_panes=False) + def test_databook_formatter_with_new_lines(self): + """Test XLSX export with new line in content.""" + self.founders.append(('First\nSecond', 'Name', 42)) + self.founders.export('xlsx') + if __name__ == '__main__': unittest.main() diff --git a/tox.ini b/tox.ini index 34003e13..3e1d6a29 100644 --- a/tox.ini +++ b/tox.ini @@ -4,7 +4,7 @@ # and then run "tox" from this directory. [tox] -envlist = py26, py27, py32, py33, py34, py35, py36, pypy +envlist = py26, py27, py33, py34, py35, py36, pypy [testenv] commands = python setup.py test