Hello community, here is the log from the commit of package python3-openpyxl for openSUSE:Factory checked in at 2015-11-23 07:30:13 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python3-openpyxl (Old) and /work/SRC/openSUSE:Factory/.python3-openpyxl.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python3-openpyxl" Changes: -------- --- /work/SRC/openSUSE:Factory/python3-openpyxl/python3-openpyxl.changes 2015-11-05 11:35:44.000000000 +0100 +++ /work/SRC/openSUSE:Factory/.python3-openpyxl.new/python3-openpyxl.changes 2015-11-23 07:30:14.000000000 +0100 @@ -1,0 +2,15 @@ +Sun Nov 22 00:55:36 UTC 2015 - a...@gmx.de + +- update to version 2.3.1: + * #534 Exception when using columns property in read-only mode. + * #536 Incorrectly handle comments from Google Docs files. + * #539 Flexible value types for conditional formatting. + * #542 Missing content types for images. + * #543 Make sure images fit containers on all OSes. + * #544 Gracefully handle missing cell styles. + * #546 ExternalLink duplicated when editing a file with macros. + * #548 Exception with non-ASCII worksheet titles + * #551 Combine multiple LineCharts + * PR 88 Fix page margins in parser. + +------------------------------------------------------------------- Old: ---- openpyxl-2.3.0.tar.gz New: ---- openpyxl-2.3.1.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python3-openpyxl.spec ++++++ --- /var/tmp/diff_new_pack.FQX20y/_old 2015-11-23 07:30:15.000000000 +0100 +++ /var/tmp/diff_new_pack.FQX20y/_new 2015-11-23 07:30:15.000000000 +0100 @@ -17,7 +17,7 @@ Name: python3-openpyxl -Version: 2.3.0 +Version: 2.3.1 Release: 0 Summary: A Python library to read/write Excel 2007 xlsx/xlsm files License: MIT and Python-2.0 ++++++ openpyxl-2.3.0.tar.gz -> openpyxl-2.3.1.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.3.0/AUTHORS.rst new/openpyxl-2.3.1/AUTHORS.rst --- old/openpyxl-2.3.0/AUTHORS.rst 2015-10-20 18:25:39.000000000 +0200 +++ new/openpyxl-2.3.1/AUTHORS.rst 2015-11-20 09:48:05.000000000 +0100 @@ -15,6 +15,7 @@ * ccoacley * Maarten De Paepe * Etienne Desautels +* Dmitriy Chernyshov * Eric Chlebek * Alexandre Fayolle * Eric Gazoni diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.3.0/PKG-INFO new/openpyxl-2.3.1/PKG-INFO --- old/openpyxl-2.3.0/PKG-INFO 2015-10-20 18:26:07.000000000 +0200 +++ new/openpyxl-2.3.1/PKG-INFO 2015-11-20 09:49:39.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: openpyxl -Version: 2.3.0 +Version: 2.3.1 Summary: A Python library to read/write Excel 2010 xlsx/xlsm files Home-page: http://openpyxl.readthedocs.org Author: See AUTHORS diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.3.0/openpyxl/.constants.json new/openpyxl-2.3.1/openpyxl/.constants.json --- old/openpyxl-2.3.0/openpyxl/.constants.json 2015-10-20 18:25:39.000000000 +0200 +++ new/openpyxl-2.3.1/openpyxl/.constants.json 2015-11-20 09:48:05.000000000 +0100 @@ -4,5 +4,5 @@ "__license__": "MIT/Expat", "__maintainer_email__": "openpyxl-us...@googlegroups.com", "__url__": "http://openpyxl.readthedocs.org", - "__version__": "2.3.0" + "__version__": "2.3.1" } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.3.0/openpyxl/cell/text.py new/openpyxl-2.3.1/openpyxl/cell/text.py --- old/openpyxl-2.3.0/openpyxl/cell/text.py 1970-01-01 01:00:00.000000000 +0100 +++ new/openpyxl-2.3.1/openpyxl/cell/text.py 2015-11-20 09:48:05.000000000 +0100 @@ -0,0 +1,180 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2015 openpyxl + +""" +Richtext definition +""" +from openpyxl.compat import unicode + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Alias, + Typed, + Integer, + Set, + Bool, + String, + Sequence, +) +from openpyxl.descriptors.nested import ( + NestedBool, + NestedInteger, + NestedString, + NestedText, +) +from openpyxl.styles.fonts import Font + + +class PhoneticProperties(Serialisable): + + fontId = Integer() + type = Set(values=(['halfwidthKatakana', 'fullwidthKatakana', 'Hiragana', + 'noConversion'])) + alignment = Set(values=(['noControl', 'left', 'center', 'distributed'])) + + def __init__(self, + fontId=None, + type=None, + alignment=None, + ): + self.fontId = fontId + self.type = type + self.alignment = alignment + + +class PhoneticText(Serialisable): + + sb = Integer() + eb = Integer() + t = Typed(expected_type=String()) + text = Alias('t') + + def __init__(self, + sb=None, + eb=None, + t=None, + ): + self.sb = sb + self.eb = eb + self.t = t + + +class InlineFont(Font): + + """ + Font for inline text because, yes what you need are different objects with the same elements but different constraints. + """ + + tagname = "RPrElt" + + rFont = NestedString(allow_none=True) + charset = Font.charset + family = Font.family + b =Font.b + i = Font.i + strike = Font.strike + outline = Font.outline + shadow = Font.shadow + condense = Font.condense + extend = Font.extend + color = Font.color + sz = Font.sz + u = Font.u + vertAlign = Font.vertAlign + scheme = Font.scheme + + __elements__ = ('rFont', 'charset', 'family', 'b', 'i', 'strike', + 'outline', 'shadow', 'condense', 'extend', 'color', 'sz', 'u', + 'vertAlign', 'scheme') + + def __init__(self, + rFont=None, + charset=None, + family=None, + b=None, + i=None, + strike=None, + outline=None, + shadow=None, + condense=None, + extend=None, + color=None, + sz=None, + u=None, + vertAlign=None, + scheme=None, + ): + self.rFont = rFont + self.charset = charset + self.family = family + self.b = b + self.i = i + self.strike = strike + self.outline = outline + self.shadow = shadow + self.condense = condense + self.extend = extend + self.color = color + self.sz = sz + self.u = u + self.vertAlign = vertAlign + self.scheme = scheme + + +class RichText(Serialisable): + + tagname = "RElt" + + rPr = Typed(expected_type=InlineFont, allow_none=True) + font = Alias("rPr") + t = NestedText(expected_type=unicode, allow_none=True) + text = Alias("t") + + __elements__ = ('rPr', 't') + + def __init__(self, + rPr=None, + t=None, + ): + self.rPr = rPr + self.t = t + + +class Text(Serialisable): + + tagname = "text" + + t = NestedText(allow_none=True, expected_type=unicode) + plain = Alias("t") + r = Sequence(expected_type=RichText, allow_none=True) + formatted = Alias("r") + rPh = Sequence(expected_type=PhoneticText, allow_none=True) + phonetic = Alias("rPh") + phoneticPr = Typed(expected_type=PhoneticProperties, allow_none=True) + PhoneticProperties = Alias("phoneticPr") + + __elements__ = ('t', 'r', 'rPh', 'phoneticPr') + + def __init__(self, + t=None, + r=(), + rPh=(), + phoneticPr=None, + ): + self.t = t + self.r = r + self.rPh = rPh + self.phoneticPr = phoneticPr + + + @property + def content(self): + """ + Text stripped of all formatting + """ + snippets = [] + if self.plain is not None: + snippets.append(self.plain) + for block in self.formatted: + snippets.append(block.t) + return "".join(snippets) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.3.0/openpyxl/chart/_chart.py new/openpyxl-2.3.1/openpyxl/chart/_chart.py --- old/openpyxl-2.3.0/openpyxl/chart/_chart.py 2015-10-20 18:25:39.000000000 +0200 +++ new/openpyxl-2.3.1/openpyxl/chart/_chart.py 2015-11-20 09:48:05.000000000 +0100 @@ -102,12 +102,16 @@ self.plot_area._charts.append(chart) idx_base += len(chart.series) - for axis in ("x_axis", "y_axis", 'z_axis'): - axis = getattr(self, axis, None) - if axis is None: - continue - ax = getattr(self.plot_area, axis.tagname) - ax.append(axis) + axIds = [] + for axId in ("x_axis", "y_axis", 'z_axis'): + for chart in self._charts: + axis = getattr(chart, axId, None) + if axis is None: + continue + if axis.axId not in axIds: + ax = getattr(self.plot_area, axis.tagname) + ax.append(axis) + axIds.append(axis.axId) container = ChartContainer(plotArea=self.plot_area, legend=self.legend, title=self.title) if isinstance(chart, _3DBase): @@ -157,7 +161,7 @@ values = data.cols for v in values: - range_string = "{0}!{1}:{2}".format(data.sheetname, v[0], v[-1]) + range_string = u"{0}!{1}:{2}".format(data.sheetname, v[0], v[-1]) series = SeriesFactory(range_string, title_from_data=titles_from_data) self.ser.append(series) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.3.0/openpyxl/chart/reference.py new/openpyxl-2.3.1/openpyxl/chart/reference.py --- old/openpyxl-2.3.0/openpyxl/chart/reference.py 2015-10-20 18:25:39.000000000 +0200 +++ new/openpyxl-2.3.1/openpyxl/chart/reference.py 2015-11-20 09:48:05.000000000 +0100 @@ -3,6 +3,7 @@ from itertools import chain +from openpyxl.compat import unicode from openpyxl.descriptors.serialisable import Serialisable from openpyxl.descriptors import ( MinMax, @@ -62,18 +63,22 @@ def __repr__(self): - fmt = "{0}!${1}${2}:${3}${4}" + return unicode(self) + + + def __str__(self): + fmt = u"{0}!${1}${2}:${3}${4}" if (self.min_col == self.max_col and self.min_row == self.max_row): - fmt = "{0}!${1}${2}" + fmt = u"{0}!${1}${2}" return fmt.format(self.sheetname, get_column_letter(self.min_col), self.min_row, get_column_letter(self.max_col), self.max_row ) - def __str__(self): - return repr(self) + __unicode__ = __str__ + def __len__(self): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.3.0/openpyxl/chart/series_factory.py new/openpyxl-2.3.1/openpyxl/chart/series_factory.py --- old/openpyxl-2.3.0/openpyxl/chart/series_factory.py 2015-10-20 18:25:39.000000000 +0200 +++ new/openpyxl-2.3.1/openpyxl/chart/series_factory.py 2015-11-20 09:48:05.000000000 +0100 @@ -17,7 +17,7 @@ if title_from_data: cell = values.pop() - title = "{0}!{1}".format(values.sheetname, cell) + title = u"{0}!{1}".format(values.sheetname, cell) title = SeriesLabel(strRef=StrRef(title)) elif title is not None: title = SeriesLabel(v=title) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.3.0/openpyxl/comments/author.py new/openpyxl-2.3.1/openpyxl/comments/author.py --- old/openpyxl-2.3.0/openpyxl/comments/author.py 1970-01-01 01:00:00.000000000 +0100 +++ new/openpyxl-2.3.1/openpyxl/comments/author.py 2015-11-20 09:48:05.000000000 +0100 @@ -0,0 +1,22 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2015 openpyxl +from openpyxl.compat import unicode + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Sequence, + Alias +) + + +class AuthorList(Serialisable): + + tagname = "authors" + + author = Sequence(expected_type=unicode) + authors = Alias("author") + + def __init__(self, + author=(), + ): + self.author = author diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.3.0/openpyxl/comments/comments.py new/openpyxl-2.3.1/openpyxl/comments/comments.py --- old/openpyxl-2.3.0/openpyxl/comments/comments.py 2015-10-20 14:55:48.000000000 +0200 +++ new/openpyxl-2.3.1/openpyxl/comments/comments.py 2015-11-20 09:48:05.000000000 +0100 @@ -3,40 +3,14 @@ class Comment(object): - __slots__ = ('_parent', - '_text', - '_author', - '_width', - '_height') - def __init__(self, text, author): - self._text = text - self._author = author - self._parent = None - self._width = '108pt' - self._height = '59.25pt' - - @property - def author(self): - """ The name recorded for the author + _parent = None - :rtype: string - """ - return self._author - @author.setter - def author(self, value): - self._author = value - - @property - def text(self): - """ The text of the commment - - :rtype: string - """ - return self._text - @text.setter - def text(self, value): - self._text = value + def __init__(self, text, author): + self.content = text + self.author = author + self.width = '108pt' + self.height = '59.25pt' @property def parent(self): @@ -47,3 +21,16 @@ if cell is not None and self._parent is not None and self._parent != cell: raise AttributeError("Comment already assigned to %s in worksheet %s. Cannot assign a comment to more than one cell" % (cell.coordinate, cell.parent.title)) self._parent = cell + + + @property + def text(self): + """ + Any comment text stripped of all formatting. + """ + return self.content + + + @text.setter + def text(self, value): + self.content = value diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.3.0/openpyxl/comments/properties.py new/openpyxl-2.3.1/openpyxl/comments/properties.py --- old/openpyxl-2.3.0/openpyxl/comments/properties.py 1970-01-01 01:00:00.000000000 +0100 +++ new/openpyxl-2.3.1/openpyxl/comments/properties.py 2015-11-20 09:48:05.000000000 +0100 @@ -0,0 +1,164 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2015 openpyxl + +## Incomplete! + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + Float, + Integer, + Set, + String, + Bool, +) +from openpyxl.descriptors.excel import Guid, ExtensionList +from openpyxl.descriptors.sequence import NestedSequence + +from openpyxl.xml.constants import SHEET_MAIN_NS + +from openpyxl.cell.text import Text +from .author import AuthorList + + +class ObjectAnchor(Serialisable): + + moveWithCells = Bool(allow_none=True) + sizeWithCells = Bool(allow_none=True) + #z-order = Integer(allow_none=True) needs alias + #from + #to defs from xdr + + def __init__(self, + moveWithCells=None, + sizeWithCells=None, + #z-order=None, + ): + self.moveWithCells = moveWithCells + self.sizeWithCells = sizeWithCells + #self.z-order = z-order + + +class Properties(Serialisable): + + locked = Bool(allow_none=True) + defaultSize = Bool(allow_none=True) + _print = Bool(allow_none=True) + disabled = Bool(allow_none=True) + uiObject = Bool(allow_none=True) + autoFill = Bool(allow_none=True) + autoLine = Bool(allow_none=True) + altText = String(allow_none=True) + textHAlign = Set(values=(['left', 'center', 'right', 'justify', 'distributed'])) + textVAlign = Set(values=(['top', 'center', 'bottom', 'justify', 'distributed'])) + lockText = Bool(allow_none=True) + justLastX = Bool(allow_none=True) + autoScale = Bool(allow_none=True) + rowHidden = Bool(allow_none=True) + colHidden = Bool(allow_none=True) + anchor = Typed(expected_type=ObjectAnchor, ) + + __elements__ = ('anchor',) + + def __init__(self, + locked=None, + defaultSize=None, + _print=None, + disabled=None, + uiObject=None, + autoFill=None, + autoLine=None, + altText=None, + textHAlign=None, + textVAlign=None, + lockText=None, + justLastX=None, + autoScale=None, + rowHidden=None, + colHidden=None, + anchor=None, + ): + self.locked = locked + self.defaultSize = defaultSize + self._print = _print + self.disabled = disabled + self.uiObject = uiObject + self.autoFill = autoFill + self.autoLine = autoLine + self.altText = altText + self.textHAlign = textHAlign + self.textVAlign = textVAlign + self.lockText = lockText + self.justLastX = justLastX + self.autoScale = autoScale + self.rowHidden = rowHidden + self.colHidden = colHidden + self.anchor = anchor + + + +class Comment(Serialisable): + + tagname = "comment" + + ref = String() + authorId = Integer() + guid = Guid(allow_none=True) + shapeId = Integer(allow_none=True) + text = Typed(expected_type=Text) + commentPr = Typed(expected_type=Properties, allow_none=True) + author = String(allow_none=True) + + __elements__ = ('text', 'commentPr') + + def __init__(self, + ref="", + authorId=0, + guid=None, + shapeId=0, + text=None, + commentPr=None, + author=None, + ): + self.ref = ref + self.authorId = authorId + self.guid = guid + self.shapeId = shapeId + if text is None: + text = Text() + self.text = text + self.commentPr = commentPr + self.author = author + + + @property + def content(self): + """ + Remove all inline formatting and stuff + """ + return self.text.content + + +class CommentSheet(Serialisable): + + tagname = "comments" + + authors = Typed(expected_type=AuthorList) + commentList = NestedSequence(expected_type=Comment, count=0) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('authors', 'commentList') + + def __init__(self, + authors=None, + commentList=None, + extLst=None, + ): + self.authors = authors + self.commentList = commentList + + + def to_tree(self): + tree = super(CommentSheet, self).to_tree() + tree.set("xmlns", SHEET_MAIN_NS) + return tree diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.3.0/openpyxl/comments/reader.py new/openpyxl-2.3.1/openpyxl/comments/reader.py --- old/openpyxl-2.3.0/openpyxl/comments/reader.py 1970-01-01 01:00:00.000000000 +0100 +++ new/openpyxl-2.3.1/openpyxl/comments/reader.py 2015-11-20 09:48:05.000000000 +0100 @@ -0,0 +1,48 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2015 openpyxl + + +import os.path + +from openpyxl.comments import Comment +from openpyxl.xml.constants import ( + PACKAGE_WORKSHEET_RELS, + COMMENTS_NS, + PACKAGE_XL, + ) +from openpyxl.xml.functions import fromstring + +from .properties import CommentSheet + + +def read_comments(ws, xml_source): + """Given a worksheet and the XML of its comments file, assigns comments to cells""" + root = fromstring(xml_source) + comments = CommentSheet.from_tree(root) + authors = comments.authors.author + + for comment in comments.commentList: + author = authors[comment.authorId] + ref = comment.ref + comment = Comment(comment.content, author) + + ws.cell(coordinate=ref).comment = comment + + +def get_comments_file(worksheet_path, archive, valid_files): + """Returns the XML filename in the archive which contains the comments for + the spreadsheet with codename sheet_codename. Returns None if there is no + such file""" + sheet_codename = os.path.split(worksheet_path)[-1] + rels_file = PACKAGE_WORKSHEET_RELS + '/' + sheet_codename + '.rels' + if rels_file not in valid_files: + return None + rels_source = archive.read(rels_file) + root = fromstring(rels_source) + for i in root: + if i.attrib['Type'] == COMMENTS_NS: + comments_file = os.path.split(i.attrib['Target'])[-1] + comments_file = PACKAGE_XL + '/' + comments_file + if comments_file in valid_files: + return comments_file + return None diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.3.0/openpyxl/comments/writer.py new/openpyxl-2.3.1/openpyxl/comments/writer.py --- old/openpyxl-2.3.0/openpyxl/comments/writer.py 1970-01-01 01:00:00.000000000 +0100 +++ new/openpyxl-2.3.1/openpyxl/comments/writer.py 2015-11-20 09:48:05.000000000 +0100 @@ -0,0 +1,117 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2015 openpyxl + + +from openpyxl.utils.indexed_list import IndexedList +from openpyxl.compat import iteritems +from openpyxl.xml.constants import SHEET_MAIN_NS +from openpyxl.xml.functions import Element, SubElement, tostring +from openpyxl.utils import ( + column_index_from_string, + coordinate_from_string, +) + +from .author import AuthorList +from .properties import CommentSheet, Comment + +vmlns = "urn:schemas-microsoft-com:vml" +officens = "urn:schemas-microsoft-com:office:office" +excelns = "urn:schemas-microsoft-com:office:excel" + + +class CommentWriter(object): + + + def __init__(self, sheet): + self.sheet = sheet + self.comments = [] + + + def write_comments(self): + """ + Create list of comments and authors + Sorted by row, col + """ + # produce xml + authors = IndexedList() + + for _coord, cell in sorted(self.sheet._cells.items()): + if cell.comment is not None: + comment = Comment(ref=cell.coordinate) + comment.authorId = authors.add(cell.comment.author) + comment.text.t = cell.comment.text + comment.height = cell.comment.height + comment.width = cell.comment.width + self.comments.append(comment) + + author_list = AuthorList(authors) + root = CommentSheet(authors=author_list, commentList=self.comments) + + return tostring(root.to_tree()) + + def write_comments_vml(self): + root = Element("xml") + shape_layout = SubElement(root, "{%s}shapelayout" % officens, + {"{%s}ext" % vmlns: "edit"}) + SubElement(shape_layout, + "{%s}idmap" % officens, + {"{%s}ext" % vmlns: "edit", "data": "1"}) + shape_type = SubElement(root, + "{%s}shapetype" % vmlns, + {"id": "_x0000_t202", + "coordsize": "21600,21600", + "{%s}spt" % officens: "202", + "path": "m,l,21600r21600,l21600,xe"}) + SubElement(shape_type, "{%s}stroke" % vmlns, {"joinstyle": "miter"}) + SubElement(shape_type, + "{%s}path" % vmlns, + {"gradientshapeok": "t", + "{%s}connecttype" % officens: "rect"}) + + for idx, comment in enumerate(self.comments, 1026): + + shape = _shape_factory() + col, row = coordinate_from_string(comment.ref) + row -= 1 + column = column_index_from_string(col) - 1 + + shape.set('id', "_x0000_s%04d" % idx) + client_data = shape.find("{%s}ClientData" % excelns) + client_data.find("{%s}Row" % excelns).text = str(row) + client_data.find("{%s}Column" % excelns).text = str(column) + root.append(shape) + + return tostring(root) + + +def _shape_factory(): + + style = ("position:absolute; margin-left:59.25pt;" + "margin-top:1.5pt;width:{width};height:{height};" + "z-index:1;visibility:hidden").format(height="59.25pt", + width="108pt") + attrs = { + "type": "#_x0000_t202", + "style": style, + "fillcolor": "#ffffe1", + "{%s}insetmode" % officens: "auto" + } + shape = Element("{%s}shape" % vmlns, attrs) + + SubElement(shape, "{%s}fill" % vmlns, + {"color2": "#ffffe1"}) + SubElement(shape, "{%s}shadow" % vmlns, + {"color": "black", "obscured": "t"}) + SubElement(shape, "{%s}path" % vmlns, + {"{%s}connecttype" % officens: "none"}) + textbox = SubElement(shape, "{%s}textbox" % vmlns, + {"style": "mso-direction-alt:auto"}) + SubElement(textbox, "div", {"style": "text-align:left"}) + client_data = SubElement(shape, "{%s}ClientData" % excelns, + {"ObjectType": "Note"}) + SubElement(client_data, "{%s}MoveWithCells" % excelns) + SubElement(client_data, "{%s}SizeWithCells" % excelns) + SubElement(client_data, "{%s}AutoFill" % excelns).text = "False" + SubElement(client_data, "{%s}Row" % excelns) + SubElement(client_data, "{%s}Column" % excelns) + return shape diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.3.0/openpyxl/descriptors/excel.py new/openpyxl-2.3.1/openpyxl/descriptors/excel.py --- old/openpyxl-2.3.0/openpyxl/descriptors/excel.py 2015-10-20 18:25:39.000000000 +0200 +++ new/openpyxl-2.3.1/openpyxl/descriptors/excel.py 2015-11-20 09:48:05.000000000 +0100 @@ -5,7 +5,6 @@ Excel specific descriptors """ -from openpyxl.compat import basestring from openpyxl.xml.constants import REL_NS from . import MatchPattern, MinMax, Integer, String, Typed, Sequence from .serialisable import Serialisable @@ -55,7 +54,7 @@ ext = Sequence(expected_type=Extension) def __init__(self, - ext=None, + ext=(), ): self.ext = ext diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.3.0/openpyxl/descriptors/sequence.py new/openpyxl-2.3.1/openpyxl/descriptors/sequence.py --- old/openpyxl-2.3.0/openpyxl/descriptors/sequence.py 2015-10-20 18:25:39.000000000 +0200 +++ new/openpyxl-2.3.1/openpyxl/descriptors/sequence.py 2015-11-20 09:48:05.000000000 +0100 @@ -3,6 +3,7 @@ from openpyxl.compat import safe_string from openpyxl.xml.functions import Element +from openpyxl.utils.indexed_list import IndexedList from .base import Descriptor, _convert from .namespace import namespaced @@ -17,12 +18,15 @@ expected_type = type(None) seq_types = (list, tuple) idx_base = 0 + unique = False def __set__(self, instance, seq): if not isinstance(seq, self.seq_types): raise TypeError("Value must be a sequence") seq = [_convert(self.expected_type, value) for value in seq] + if self.unique: + seq = IndexedList(seq) super(Sequence, self).__set__(instance, seq) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.3.0/openpyxl/drawing/fill.py new/openpyxl-2.3.1/openpyxl/drawing/fill.py --- old/openpyxl-2.3.0/openpyxl/drawing/fill.py 2015-10-20 18:25:39.000000000 +0200 +++ new/openpyxl-2.3.1/openpyxl/drawing/fill.py 2015-11-20 09:48:05.000000000 +0100 @@ -29,6 +29,7 @@ class PatternFillProperties(Serialisable): tagname = "pattFill" + namespace = DRAWING_NS prst = NoneSet(values=(['pct5', 'pct10', 'pct20', 'pct25', 'pct30', 'pct40', 'pct50', 'pct60', 'pct70', 'pct75', 'pct80', 'pct90', 'horz', 'vert', @@ -91,7 +92,7 @@ fillRect = Typed(expected_type=RelativeRect, allow_none=True) def __init__(self, - fillRect=None, + fillRect=RelativeRect(), ): self.fillRect = fillRect @@ -336,7 +337,7 @@ rotWithShape=None, blip=None, tile=None, - stretch=None, + stretch=StretchInfoProperties(), srcRect=None, ): self.dpi = dpi diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.3.0/openpyxl/formatting/rule.py new/openpyxl-2.3.1/openpyxl/formatting/rule.py --- old/openpyxl-2.3.0/openpyxl/formatting/rule.py 2015-10-20 18:25:39.000000000 +0200 +++ new/openpyxl-2.3.1/openpyxl/formatting/rule.py 2015-11-20 09:48:05.000000000 +0100 @@ -9,18 +9,27 @@ String, Sequence, Bool, - Float, NoneSet, Set, - Integer,) -from openpyxl.descriptors.excel import HexBinary + Integer, + Float, +) +from openpyxl.descriptors.excel import HexBinary, ExtensionList from openpyxl.styles.colors import Color, ColorDescriptor from openpyxl.styles.differential import DifferentialStyle -class ExtensionList(Serialisable): - - pass +class ValueDescriptor(Float): + """ + Expected type depends upon type attribue of parent :-( + """ + + def __set__(self, instance, value): + if instance.type == "formula": + self.expected_type = basestring + else: + self.expected_type = float + super(ValueDescriptor, self).__set__(instance, value) class FormatObject(Serialisable): @@ -28,10 +37,12 @@ tagname = "cfvo" type = Set(values=(['num', 'percent', 'max', 'min', 'formula', 'percentile'])) - val = Integer(allow_none=True) + val = ValueDescriptor(allow_none=True) gte = Bool(allow_none=True) extLst = Typed(expected_type=ExtensionList, allow_none=True) + __elements__ = () + def __init__(self, type, val=None, @@ -41,7 +52,6 @@ self.type = type self.val = val self.gte = gte - self.extLst = extLst class RuleType(Serialisable): @@ -150,7 +160,7 @@ extLst = Typed(expected_type=ExtensionList, allow_none=True) dxf = Typed(expected_type=DifferentialStyle, allow_none=True) - __elements__ = ('colorScale', 'dataBar', 'extLst', 'iconSet', 'formula') + __elements__ = ('colorScale', 'dataBar', 'iconSet', 'formula') def __init__(self, type, @@ -190,7 +200,6 @@ self.colorScale = colorScale self.dataBar = dataBar self.iconSet = iconSet - self.extLst = extLst self.dxf = dxf diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.3.0/openpyxl/packaging/manifest.py new/openpyxl-2.3.1/openpyxl/packaging/manifest.py --- old/openpyxl-2.3.0/openpyxl/packaging/manifest.py 2015-10-20 18:25:39.000000000 +0200 +++ new/openpyxl-2.3.1/openpyxl/packaging/manifest.py 2015-11-20 09:48:05.000000000 +0100 @@ -41,6 +41,7 @@ mimetypes.add_type('application/vnd.openxmlformats-package.relationships+xml', ".rels") mimetypes.add_type("application/vnd.ms-office.activeX", ".bin") mimetypes.add_type("application/vnd.openxmlformats-officedocument.vmlDrawing", ".vml") +mimetypes.add_type("image/x-emf", ".emf") class FileExtension(Serialisable): @@ -71,6 +72,10 @@ self.ContentType = ContentType + def __hash__(self): + return hash((self.PartName, self.ContentType)) + + DEFAULT_TYPES = [ FileExtension("rels", "application/vnd.openxmlformats-package.relationships+xml"), FileExtension("xml", "application/xml"), @@ -90,14 +95,14 @@ tagname = "Types" - Default = Sequence(expected_type=FileExtension) - Override = Sequence(expected_type=Override) + Default = Sequence(expected_type=FileExtension, unique=True) + Override = Sequence(expected_type=Override, unique=True) __elements__ = ("Default", "Override") def __init__(self, Default=(), - Override=() + Override=(), ): if not Default: Default = DEFAULT_TYPES @@ -132,15 +137,22 @@ return tree -def write_content_types(workbook, as_template=False): +def write_content_types(workbook, as_template=False, exts=None): - seen = set() manifest = Manifest() + + if exts is not None: + for ext in exts: + ext = os.path.splitext(ext)[-1] + mime = mimetypes.types_map[ext] + fe = FileExtension(ext[1:], mime) + if fe not in manifest.Default: + manifest.Default.append(fe) + if workbook.vba_archive: node = fromstring(workbook.vba_archive.read(ARC_CONTENT_TYPES)) manifest = Manifest.from_tree(node) del node - seen = set(manifest.filenames) # templates for part in manifest.Override: @@ -159,21 +171,18 @@ # worksheets for sheet_id, sheet in enumerate(workbook.worksheets): name = '/xl/worksheets/sheet%d.xml' % (sheet_id + 1) - if name not in seen: - manifest.Override.append(Override(name, WORKSHEET_TYPE)) + manifest.Override.append(Override(name, WORKSHEET_TYPE)) if sheet._charts or sheet._images: drawing_id += 1 name = '/xl/drawings/drawing%d.xml' % drawing_id - if name not in seen: - manifest.Override.append(Override(name, DRAWING_TYPE)) + manifest.Override.append(Override(name, DRAWING_TYPE)) for chart in sheet._charts: chart_id += 1 name = '/xl/charts/chart%d.xml' % chart_id - if name not in seen: - manifest.Override.append(Override(name, CHART_TYPE)) + manifest.Override.append(Override(name, CHART_TYPE)) if sheet._comment_count > 0: comments_id += 1 @@ -181,27 +190,23 @@ if vml not in manifest.Default: manifest.Default.append(vml) name = '/xl/comments%d.xml' % comments_id - if name not in seen: - manifest.Override.append(Override(name, COMMENTS_TYPE)) + manifest.Override.append(Override(name, COMMENTS_TYPE)) # chartsheets for sheet_id, sheet in enumerate(workbook.chartsheets, sheet_id+1): name = '/xl/chartsheets/sheet%d.xml' % (sheet_id) - if name not in seen: - manifest.Override.append(Override(name, CHARTSHEET_TYPE)) + manifest.Override.append(Override(name, CHARTSHEET_TYPE)) if sheet._charts: drawing_id += 1 name = '/xl/drawings/drawing%d.xml' % drawing_id - if name not in seen: - manifest.Override.append(Override(name, DRAWING_TYPE)) + manifest.Override.append(Override(name, DRAWING_TYPE)) for chart in sheet._charts: chart_id += 1 name = '/xl/charts/chart%d.xml' % chart_id - if name not in seen: - manifest.Override.append(Override(name, CHART_TYPE)) + manifest.Override.append(Override(name, CHART_TYPE)) #external links for idx, _ in enumerate(workbook._external_links, 1): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.3.0/openpyxl/reader/comments.py new/openpyxl-2.3.1/openpyxl/reader/comments.py --- old/openpyxl-2.3.0/openpyxl/reader/comments.py 2015-10-20 14:55:48.000000000 +0200 +++ new/openpyxl-2.3.1/openpyxl/reader/comments.py 1970-01-01 01:00:00.000000000 +0100 @@ -1,54 +0,0 @@ -from __future__ import absolute_import -# Copyright (c) 2010-2015 openpyxl - - -import os.path - -from openpyxl.comments import Comment -from openpyxl.xml.constants import ( - PACKAGE_WORKSHEET_RELS, - SHEET_MAIN_NS, - COMMENTS_NS, - PACKAGE_XL, - ) -from openpyxl.xml.functions import fromstring, safe_iterator - -def _get_author_list(root): - author_subtree = root.find('{%s}authors' % SHEET_MAIN_NS) - return [author.text for author in author_subtree] - -def read_comments(ws, xml_source): - """Given a worksheet and the XML of its comments file, assigns comments to cells""" - root = fromstring(xml_source) - authors = _get_author_list(root) - comment_nodes = safe_iterator(root, ('{%s}comment' % SHEET_MAIN_NS)) - for node in comment_nodes: - author = authors[int(node.attrib['authorId'])] - cell = node.attrib['ref'] - text_node = node.find('{%s}text' % SHEET_MAIN_NS) - substrs = [] - for run in text_node.findall('{%s}r' % SHEET_MAIN_NS): - runtext = ''.join([t.text for t in run.findall('{%s}t' % SHEET_MAIN_NS)]) - substrs.append(runtext) - comment_text = ''.join(substrs) - - comment = Comment(comment_text, author) - ws.cell(coordinate=cell).comment = comment - -def get_comments_file(worksheet_path, archive, valid_files): - """Returns the XML filename in the archive which contains the comments for - the spreadsheet with codename sheet_codename. Returns None if there is no - such file""" - sheet_codename = os.path.split(worksheet_path)[-1] - rels_file = PACKAGE_WORKSHEET_RELS + '/' + sheet_codename + '.rels' - if rels_file not in valid_files: - return None - rels_source = archive.read(rels_file) - root = fromstring(rels_source) - for i in root: - if i.attrib['Type'] == COMMENTS_NS: - comments_file = os.path.split(i.attrib['Target'])[-1] - comments_file = PACKAGE_XL + '/' + comments_file - if comments_file in valid_files: - return comments_file - return None diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.3.0/openpyxl/reader/excel.py new/openpyxl-2.3.1/openpyxl/reader/excel.py --- old/openpyxl-2.3.0/openpyxl/reader/excel.py 2015-10-20 18:25:39.000000000 +0200 +++ new/openpyxl-2.3.1/openpyxl/reader/excel.py 2015-11-20 09:48:05.000000000 +0100 @@ -50,7 +50,7 @@ from openpyxl.workbook.properties import read_properties, DocumentProperties from openpyxl.worksheet.read_only import ReadOnlyWorksheet from .worksheet import WorkSheetParser -from .comments import read_comments, get_comments_file +from openpyxl.comments.reader import read_comments, get_comments_file # Use exc_info for Python 2 compatibility with "except Exception[,/ as] e" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.3.0/openpyxl/reader/strings.py new/openpyxl-2.3.1/openpyxl/reader/strings.py --- old/openpyxl-2.3.0/openpyxl/reader/strings.py 2015-10-20 14:55:48.000000000 +0200 +++ new/openpyxl-2.3.1/openpyxl/reader/strings.py 2015-11-20 09:48:05.000000000 +0100 @@ -1,44 +1,27 @@ from __future__ import absolute_import # Copyright (c) 2010-2015 openpyxl -"""Read the shared strings table.""" +from openpyxl.cell.text import Text +from openpyxl.utils.indexed_list import IndexedList -from openpyxl.compat import unicode +from openpyxl.xml.functions import iterparse +from openpyxl.xml.constants import SHEET_MAIN_NS -# package imports -from openpyxl.utils.indexed_list import IndexedList -from openpyxl.xml.functions import fromstring, safe_iterator -from openpyxl.xml.constants import SHEET_MAIN_NS, XML_NS +from .worksheet import _get_xml_iter def read_string_table(xml_source): """Read in all shared strings in the table""" - root = fromstring(text=xml_source) - nodes = safe_iterator(root, '{%s}si' % SHEET_MAIN_NS) - strings = (get_string(node) for node in nodes) - return IndexedList(strings) + strings = [] + src = _get_xml_iter(xml_source) + for _, node in iterparse(src): + if node.tag == '{%s}si' % SHEET_MAIN_NS: -def get_string(string_index_node): - """Read the contents of a specific string index""" - rich_nodes = string_index_node.findall('{%s}r' % SHEET_MAIN_NS) - if rich_nodes: - reconstructed_text = [] - for rich_node in rich_nodes: - partial_text = get_text(rich_node) - reconstructed_text.append(partial_text) - return unicode(''.join(reconstructed_text)) - return get_text(string_index_node) - - -def get_text(rich_node): - """Read rich text, discarding formatting if not disallowed""" - text_node = rich_node.find('{%s}t' % SHEET_MAIN_NS) - text = text_node.text or unicode('') - - if text_node.get('{%s}space' % XML_NS) != 'preserve': - text = text.strip() - - # fix XML escaping sequence for '_x' - text = text.replace('x005F_', '') - return unicode(text) + text = Text.from_tree(node).content + text = text.replace('x005F_', '') + strings.append(text) + + node.clear() + + return IndexedList(strings) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.3.0/openpyxl/reader/style.py new/openpyxl-2.3.1/openpyxl/reader/style.py --- old/openpyxl-2.3.0/openpyxl/reader/style.py 2015-10-20 18:25:39.000000000 +0200 +++ new/openpyxl-2.3.1/openpyxl/reader/style.py 2015-11-20 09:48:05.000000000 +0100 @@ -125,7 +125,7 @@ """ node = self.root.find("{%s}cellStyles" % SHEET_MAIN_NS) names = {} - for _name in node: + for _name in safe_iterator(node, '{%s}cellStyle' % SHEET_MAIN_NS): name = _name.get("name") style = NamedStyle(name=name, builtinId=_name.get("builtinId"), diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.3.0/openpyxl/reader/worksheet.py new/openpyxl-2.3.1/openpyxl/reader/worksheet.py --- old/openpyxl-2.3.0/openpyxl/reader/worksheet.py 2015-10-20 18:25:39.000000000 +0200 +++ new/openpyxl-2.3.1/openpyxl/reader/worksheet.py 2015-11-20 09:48:05.000000000 +0100 @@ -245,7 +245,7 @@ self.ws.print_options = PrintOptions.from_tree(element) def parse_margins(self, element): - self.page_margins = PageMargins.from_tree(element) + self.ws.page_margins = PageMargins.from_tree(element) def parse_page_setup(self, element): self.ws.page_setup = PrintPageSetup.from_tree(element) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.3.0/openpyxl/styles/fonts.py new/openpyxl-2.3.1/openpyxl/styles/fonts.py --- old/openpyxl-2.3.0/openpyxl/styles/fonts.py 2015-10-20 18:25:39.000000000 +0200 +++ new/openpyxl-2.3.1/openpyxl/styles/fonts.py 2015-11-20 09:48:05.000000000 +0100 @@ -35,8 +35,8 @@ name = NestedString() charset = NestedInteger(allow_none=True) - family = NestedMinMax(min=0, max=14) - sz = NestedFloat() + family = NestedMinMax(min=0, max=14, allow_none=True) + sz = NestedFloat(allow_none=True) size = Alias("sz") b = NestedBool(to_tree=_no_value) bold = Alias("b") @@ -52,7 +52,7 @@ 'doubleAccounting')) underline = Alias("u") vertAlign = NestedNoneSet(values=('superscript', 'subscript', 'baseline')) - color = ColorDescriptor() + color = ColorDescriptor(allow_none=True) scheme = NestedNoneSet(values=("major", "minor")) tagname = "font" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.3.0/openpyxl/utils/__init__.py new/openpyxl-2.3.1/openpyxl/utils/__init__.py --- old/openpyxl-2.3.0/openpyxl/utils/__init__.py 2015-10-20 18:25:39.000000000 +0200 +++ new/openpyxl-2.3.1/openpyxl/utils/__init__.py 2015-11-20 09:48:05.000000000 +0100 @@ -179,5 +179,5 @@ def quote_sheetname(sheetname): if " " in sheetname: - sheetname = "'{0}'".format(sheetname) + sheetname = u"'{0}'".format(sheetname) return sheetname diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.3.0/openpyxl/utils/datetime.py new/openpyxl-2.3.1/openpyxl/utils/datetime.py --- old/openpyxl-2.3.0/openpyxl/utils/datetime.py 2015-10-20 18:25:39.000000000 +0200 +++ new/openpyxl-2.3.1/openpyxl/utils/datetime.py 2015-11-20 09:48:05.000000000 +0100 @@ -70,8 +70,6 @@ return datetime.datetime(*parts[:3] + [0]) -UTC = tzinfo(timedelta(0), offset=0) - class GMT(tzinfo): def utcoffset(self, dt): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.3.0/openpyxl/worksheet/read_only.py new/openpyxl-2.3.1/openpyxl/worksheet/read_only.py --- old/openpyxl-2.3.0/openpyxl/worksheet/read_only.py 2015-10-20 18:25:39.000000000 +0200 +++ new/openpyxl-2.3.1/openpyxl/worksheet/read_only.py 2015-11-20 09:48:05.000000000 +0100 @@ -186,7 +186,7 @@ def columns(self): if self.max_column is None: self.calculate_dimension() - return super(IterableWorksheet, self).columns + return super(ReadOnlyWorksheet, self).columns def calculate_dimension(self, force=False): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.3.0/openpyxl/worksheet/worksheet.py new/openpyxl-2.3.1/openpyxl/worksheet/worksheet.py --- old/openpyxl-2.3.0/openpyxl/worksheet/worksheet.py 2015-10-20 18:25:39.000000000 +0200 +++ new/openpyxl-2.3.1/openpyxl/worksheet/worksheet.py 2015-11-20 09:48:05.000000000 +0100 @@ -704,18 +704,19 @@ @property def rows(self): """Iterate over all rows in the worksheet""" + if self.min_row == self.max_row == self.min_column == self.max_column: + return ((),) return tuple(self.iter_rows()) + @property def columns(self): """Iterate over all columns in the worksheet""" - max_row = self.max_row - min_row = 1 - if not self._cells: + if self.min_row == self.max_row == self.min_column == self.max_column: return ((),) cols = [] for col_idx in range(self.max_column): - cells = self.get_squared_range(col_idx + 1, min_row, col_idx + 1, max_row) + cells = self.get_squared_range(col_idx + 1, self.min_row, col_idx + 1, self.max_row) col = chain.from_iterable(cells) cols.append(tuple(col)) return tuple(cols) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.3.0/openpyxl/writer/comments.py new/openpyxl-2.3.1/openpyxl/writer/comments.py --- old/openpyxl-2.3.0/openpyxl/writer/comments.py 2015-10-20 18:25:39.000000000 +0200 +++ new/openpyxl-2.3.1/openpyxl/writer/comments.py 1970-01-01 01:00:00.000000000 +0100 @@ -1,122 +0,0 @@ -from __future__ import absolute_import -# Copyright (c) 2010-2015 openpyxl - - -from openpyxl.utils.indexed_list import IndexedList -from openpyxl.compat import iteritems -from openpyxl.xml.constants import SHEET_MAIN_NS -from openpyxl.xml.functions import Element, SubElement, tostring -from openpyxl.utils import ( - column_index_from_string, - coordinate_from_string, -) - -vmlns = "urn:schemas-microsoft-com:vml" -officens = "urn:schemas-microsoft-com:office:office" -excelns = "urn:schemas-microsoft-com:office:excel" - - -class CommentWriter(object): - - def extract_comments(self): - """ - extract list of comments and authors - """ - for _coord, cell in iteritems(self.sheet._cells): - if cell.comment is not None: - self.authors.add(cell.comment.author) - self.comments.append(cell.comment) - - def __init__(self, sheet): - self.sheet = sheet - self.authors = IndexedList() - self.comments = [] - - self.extract_comments() - - - def write_comments(self): - # produce xml - root = Element("{%s}comments" % SHEET_MAIN_NS) - authorlist_tag = SubElement(root, "{%s}authors" % SHEET_MAIN_NS) - for author in self.authors: - leaf = SubElement(authorlist_tag, "{%s}author" % SHEET_MAIN_NS) - leaf.text = author - - commentlist_tag = SubElement(root, "{%s}commentList" % SHEET_MAIN_NS) - for comment in self.comments: - attrs = {'ref': comment._parent.coordinate, - 'authorId': '%d' % self.authors.index(comment.author), - 'shapeId': '0'} - comment_tag = SubElement(commentlist_tag, - "{%s}comment" % SHEET_MAIN_NS, attrs) - - text_tag = SubElement(comment_tag, "{%s}text" % SHEET_MAIN_NS) - run_tag = SubElement(text_tag, "{%s}r" % SHEET_MAIN_NS) - SubElement(run_tag, "{%s}rPr" % SHEET_MAIN_NS) - t_tag = SubElement(run_tag, "{%s}t" % SHEET_MAIN_NS) - t_tag.text = comment.text - - return tostring(root) - - def write_comments_vml(self): - root = Element("xml") - shape_layout = SubElement(root, "{%s}shapelayout" % officens, - {"{%s}ext" % vmlns: "edit"}) - SubElement(shape_layout, - "{%s}idmap" % officens, - {"{%s}ext" % vmlns: "edit", "data": "1"}) - shape_type = SubElement(root, - "{%s}shapetype" % vmlns, - {"id": "_x0000_t202", - "coordsize": "21600,21600", - "{%s}spt" % officens: "202", - "path": "m,l,21600r21600,l21600,xe"}) - SubElement(shape_type, "{%s}stroke" % vmlns, {"joinstyle": "miter"}) - SubElement(shape_type, - "{%s}path" % vmlns, - {"gradientshapeok": "t", - "{%s}connecttype" % officens: "rect"}) - - for i, comment in enumerate(self.comments, 1026): - shape = self._write_comment_shape(comment, i) - root.append(shape) - - return tostring(root) - - def _write_comment_shape(self, comment, idx): - # get zero-indexed coordinates of the comment - col, row = coordinate_from_string(comment._parent.coordinate) - row -= 1 - column = column_index_from_string(col) - 1 - - style = ("position:absolute; margin-left:59.25pt;" - "margin-top:1.5pt;width:%(width)s;height:%(height)s;" - "z-index:1;visibility:hidden") % {'height': comment._height, - 'width': comment._width} - attrs = { - "id": "_x0000_s%04d" % idx , - "type": "#_x0000_t202", - "style": style, - "fillcolor": "#ffffe1", - "{%s}insetmode" % officens: "auto" - } - shape = Element("{%s}shape" % vmlns, attrs) - - SubElement(shape, "{%s}fill" % vmlns, - {"color2": "#ffffe1"}) - SubElement(shape, "{%s}shadow" % vmlns, - {"color": "black", "obscured": "t"}) - SubElement(shape, "{%s}path" % vmlns, - {"{%s}connecttype" % officens: "none"}) - textbox = SubElement(shape, "{%s}textbox" % vmlns, - {"style": "mso-direction-alt:auto"}) - SubElement(textbox, "div", {"style": "text-align:left"}) - client_data = SubElement(shape, "{%s}ClientData" % excelns, - {"ObjectType": "Note"}) - SubElement(client_data, "{%s}MoveWithCells" % excelns) - SubElement(client_data, "{%s}SizeWithCells" % excelns) - SubElement(client_data, "{%s}AutoFill" % excelns).text = "False" - SubElement(client_data, "{%s}Row" % excelns).text = "%d" % row - SubElement(client_data, "{%s}Column" % excelns).text = "%d" % column - return shape diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.3.0/openpyxl/writer/excel.py new/openpyxl-2.3.1/openpyxl/writer/excel.py --- old/openpyxl-2.3.0/openpyxl/writer/excel.py 2015-10-20 18:25:39.000000000 +0200 +++ new/openpyxl-2.3.1/openpyxl/writer/excel.py 2015-11-20 09:48:05.000000000 +0100 @@ -45,7 +45,7 @@ write_external_book_rel ) -from openpyxl.writer.comments import CommentWriter +from openpyxl.comments.writer import CommentWriter ARC_VBA = ('xl/vba', r'xl/drawings/.*vmlDrawing\d\.vml', 'xl/ctrlProps', 'customUI', 'xl/activeX', r'xl/media/.*\.emf') @@ -89,7 +89,12 @@ self._write_string_table(archive) self._write_external_links(archive) archive.writestr(ARC_STYLE, self.style_writer.write_table()) - manifest = write_content_types(self.workbook, as_template=as_template) + + exts = [] + for n in archive.namelist(): + if "media" in n: + exts.append(n) + manifest = write_content_types(self.workbook, as_template=as_template, exts=exts) archive.writestr(ARC_CONTENT_TYPES, tostring(manifest.to_tree())) def _write_string_table(self, archive): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.3.0/openpyxl/writer/write_only.py new/openpyxl-2.3.1/openpyxl/writer/write_only.py --- old/openpyxl-2.3.0/openpyxl/writer/write_only.py 2015-10-20 18:25:39.000000000 +0200 +++ new/openpyxl-2.3.1/openpyxl/writer/write_only.py 2015-11-20 09:48:05.000000000 +0100 @@ -15,7 +15,7 @@ from openpyxl.utils.exceptions import WorkbookAlreadySaved from openpyxl.writer.excel import ExcelWriter -from openpyxl.writer.comments import CommentWriter +from openpyxl.comments.writer import CommentWriter from .relations import write_rels from .worksheet import ( write_autofilter, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.3.0/openpyxl.egg-info/PKG-INFO new/openpyxl-2.3.1/openpyxl.egg-info/PKG-INFO --- old/openpyxl-2.3.0/openpyxl.egg-info/PKG-INFO 2015-10-20 18:26:07.000000000 +0200 +++ new/openpyxl-2.3.1/openpyxl.egg-info/PKG-INFO 2015-11-20 09:49:39.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: openpyxl -Version: 2.3.0 +Version: 2.3.1 Summary: A Python library to read/write Excel 2010 xlsx/xlsm files Home-page: http://openpyxl.readthedocs.org Author: See AUTHORS diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.3.0/openpyxl.egg-info/SOURCES.txt new/openpyxl-2.3.1/openpyxl.egg-info/SOURCES.txt --- old/openpyxl-2.3.0/openpyxl.egg-info/SOURCES.txt 2015-10-20 18:26:07.000000000 +0200 +++ new/openpyxl-2.3.1/openpyxl.egg-info/SOURCES.txt 2015-11-20 09:49:39.000000000 +0100 @@ -16,6 +16,7 @@ openpyxl/cell/cell.py openpyxl/cell/interface.py openpyxl/cell/read_only.py +openpyxl/cell/text.py openpyxl/chart/_3d.py openpyxl/chart/__init__.py openpyxl/chart/_chart.py @@ -55,7 +56,11 @@ openpyxl/chartsheet/relation.py openpyxl/chartsheet/views.py openpyxl/comments/__init__.py +openpyxl/comments/author.py openpyxl/comments/comments.py +openpyxl/comments/properties.py +openpyxl/comments/reader.py +openpyxl/comments/writer.py openpyxl/compat/__init__.py openpyxl/compat/abc.py openpyxl/compat/functools.py @@ -94,7 +99,6 @@ openpyxl/packaging/manifest.py openpyxl/packaging/relationship.py openpyxl/reader/__init__.py -openpyxl/reader/comments.py openpyxl/reader/excel.py openpyxl/reader/strings.py openpyxl/reader/style.py @@ -147,7 +151,6 @@ openpyxl/worksheet/views.py openpyxl/worksheet/worksheet.py openpyxl/writer/__init__.py -openpyxl/writer/comments.py openpyxl/writer/dump_worksheet.py openpyxl/writer/etree_worksheet.py openpyxl/writer/excel.py