multisplit
[iramuteq] / ooolib.py
index d683b50..f54b499 100644 (file)
--- a/ooolib.py
+++ b/ooolib.py
@@ -1,5 +1,10 @@
+# -*- coding: utf-8 -*-
+#modification pour python 3 : Laurent Mérat, 6x7 - mai 2020
+
 "ooolib-python - Copyright (C) 2006-2009 Joseph Colton"
 
+# PEUT-ETRE une MISE A JOUR ???
+
 # ooolib-python - Python module for creating Open Document Format documents.
 # Copyright (C) 2006-2009  Joseph Colton
 
@@ -29,1934 +34,1845 @@ import re
 import xml.parsers.expat # Needed for parsing documents
 
 def version_number():
-       "Get the ooolib-python version number"
-       return "0.0.17"
+    "Get the ooolib-python version number"
+    return "0.0.17"
 
 def version():
-       "Get the ooolib-python version"
-       return "ooolib-python-%s" % version_number()
+    "Get the ooolib-python version"
+    return "ooolib-python-%s" % version_number()
 
 def clean_string(data):
-       "Returns an XML friendly copy of the data string"
-       
-       data = unicode(data) # This line thanks to Chris Ender
-       
-       data = data.replace('&', '&')
-       data = data.replace("'", ''')
-       data = data.replace('"', '"')
-       data = data.replace('<', '&lt;')
-       data = data.replace('>', '&gt;')
-       data = data.replace('\t', '<text:tab-stop/>')
-       data = data.replace('\n', '<text:line-break/>')
-       return data
+    "Returns an XML friendly copy of the data string"
+    data = str(data) # This line thanks to Chris Ender
+    data = data.replace('&', '&amp;')
+    data = data.replace("'", '&apos;')
+    data = data.replace('"', '&quot;')
+    data = data.replace('<', '&lt;')
+    data = data.replace('>', '&gt;')
+    data = data.replace('\t', '<text:tab-stop/>')
+    data = data.replace('\n', '<text:line-break/>')
+    return data
+
 
 class XML:
-       "XML Class - Used to convert nested lists into XML"
-       def __init__(self):
-               "Initialize ooolib XML instance"
-               pass
-
-       def _xmldata(self, data):
-               datatype = data.pop(0)
-               datavalue = data.pop(0)
-               outstring = '%s' % datavalue
-               return outstring
-
-       def _xmltag(self, data):
-               outstring = ''
-               # First two
-               datatype = data.pop(0)
-               dataname = data.pop(0)
-               outstring = '<%s' % dataname
-               # Element Section
-               element = 1
-               while(data):
-                       # elements
-                       newdata = data.pop(0)
-                       if (newdata[0] == 'element' and element):
-                               newstring = self._xmlelement(newdata)
-                               outstring = '%s %s' % (outstring, newstring)
-                               continue
-                       if (newdata[0] != 'element' and element):
-                               element = 0
-                               outstring = '%s>' % outstring
-                               if (newdata[0] == 'tag' or newdata[0] == 'tagline'):
-                                       outstring = '%s\n' % outstring
-                       if (newdata[0] == 'tag'):
-                               newstring = self._xmltag(newdata)
-                               outstring = '%s%s' % (outstring, newstring)
-                               continue
-                       if (newdata[0] == 'tagline'):
-                               newstring = self._xmltagline(newdata)
-                               outstring = '%s%s' % (outstring, newstring)
-                               continue
-                       if (newdata[0] == 'data'):
-                               newstring = self._xmldata(newdata)
-                               outstring = '%s%s' % (outstring, newstring)
-                               continue
-               if (element):
-                       element = 0
-                       outstring = '%s>\n' % outstring
-               outstring = '%s</%s>\n' % (outstring, dataname)
-               return outstring
-
-       def _xmltagline(self, data):
-               outstring = ''
-               # First two
-               datatype = data.pop(0)
-               dataname = data.pop(0)
-               outstring = '<%s' % dataname
-               # Element Section
-               while(data):
-                       # elements
-                       newdata = data.pop(0)
-                       if (newdata[0] != 'element'): break
-                       newstring = self._xmlelement(newdata)
-                       outstring = '%s %s' % (outstring, newstring)
-               outstring = '%s/>\n' % outstring
-               # Non-Element Section should not exist
-               return outstring
-
-       def _xmlelement(self, data):
-               datatype = data.pop(0)
-               dataname = data.pop(0)
-               datavalue = data.pop(0)
-               outstring = '%s="%s"' % (dataname, datavalue)   
-               return outstring
-
-       def convert(self, data):
-               """Convert nested lists into XML
-
-               The convert method takes a nested lists and converts them
-               into XML to be used in Open Document Format documents.
-               There are three types of lists that are recognized at this
-               time.  They are as follows:
-
-               'tag' - Tag opens a set of data that is eventually closed
-               with a similar tag.
-               List: ['tag', 'xml']
-               XML: <xml></xml>
-
-               'tagline' - Taglines are similar to tags, except they open
-               and close themselves.
-               List: ['tagline', 'xml']
-               XML: <xml/>
-
-               'element' - Elements are pieces of information stored in an
-               opening tag or tagline.
-               List: ['element', 'color', 'blue']
-               XML: color="blue"
-
-               'data' - Data is plain text directly inserted into the XML
-               document.
-               List: ['data', 'hello']
-               XML: hello
-
-               Bring them all together for something like this.
-
-               Lists:
-               ['tag', 'xml', ['element', 'a', 'b'], ['tagline', 'xml2'], 
-               ['data', 'asdf']]
-
-               XML:
-               <xml a="b"><xml2/>asdf</xml>
-               """
-               outlines = []
-               outlines.append('<?xml version="1.0" encoding="UTF-8"?>')
-               if (type(data) == type([]) and len(data) > 0):
-                       if data[0] == 'tag': outlines.append(self._xmltag(data))
-               return outlines
+    "XML Class - Used to convert nested lists into XML"
+
+    def __init__(self):
+        "Initialize ooolib XML instance"
+        pass
+
+    def _xmldata(self, data):
+        datatype = data.pop(0)
+        datavalue = data.pop(0)
+        outstring = '%s' % datavalue
+        return outstring
+
+    def _xmltag(self, data):
+        outstring = ''
+        # First two
+        datatype = data.pop(0)
+        dataname = data.pop(0)
+        outstring = '<%s' % dataname
+        # Element Section
+        element = 1
+        while(data):
+            # elements
+            newdata = data.pop(0)
+            if (newdata[0] == 'element' and element):
+                newstring = self._xmlelement(newdata)
+                outstring = '%s %s' % (outstring, newstring)
+                continue
+            if (newdata[0] != 'element' and element):
+                element = 0
+                outstring = '%s>' % outstring
+                if (newdata[0] == 'tag' or newdata[0] == 'tagline'):
+                    outstring = '%s\n' % outstring
+            if (newdata[0] == 'tag'):
+                newstring = self._xmltag(newdata)
+                outstring = '%s%s' % (outstring, newstring)
+                continue
+            if (newdata[0] == 'tagline'):
+                newstring = self._xmltagline(newdata)
+                outstring = '%s%s' % (outstring, newstring)
+                continue
+            if (newdata[0] == 'data'):
+                newstring = self._xmldata(newdata)
+                outstring = '%s%s' % (outstring, newstring)
+                continue
+        if (element):
+            element = 0
+            outstring = '%s>\n' % outstring
+        outstring = '%s</%s>\n' % (outstring, dataname)
+        return outstring
+
+    def _xmltagline(self, data):
+        outstring = ''
+        # First two
+        datatype = data.pop(0)
+        dataname = data.pop(0)
+        outstring = '<%s' % dataname
+        # Element Section
+        while(data):
+            # elements
+            newdata = data.pop(0)
+            if (newdata[0] != 'element'): break
+            newstring = self._xmlelement(newdata)
+            outstring = '%s %s' % (outstring, newstring)
+        outstring = '%s/>\n' % outstring
+        # Non-Element Section should not exist
+        return outstring
+
+    def _xmlelement(self, data):
+        datatype = data.pop(0)
+        dataname = data.pop(0)
+        datavalue = data.pop(0)
+        outstring = '%s="%s"' % (dataname, datavalue)    
+        return outstring
+
+    def convert(self, data):
+        """Convert nested lists into XML
+
+        The convert method takes a nested lists and converts them
+        into XML to be used in Open Document Format documents.
+        There are three types of lists that are recognized at this
+        time.  They are as follows:
+
+        'tag' - Tag opens a set of data that is eventually closed
+        with a similar tag.
+        List: ['tag', 'xml']
+        XML: <xml></xml>
+
+        'tagline' - Taglines are similar to tags, except they open
+        and close themselves.
+        List: ['tagline', 'xml']
+        XML: <xml/>
+
+        'element' - Elements are pieces of information stored in an
+        opening tag or tagline.
+        List: ['element', 'color', 'blue']
+        XML: color="blue"
+
+        'data' - Data is plain text directly inserted into the XML
+        document.
+        List: ['data', 'hello']
+        XML: hello
+
+        Bring them all together for something like this.
+
+        Lists:
+        ['tag', 'xml', ['element', 'a', 'b'], ['tagline', 'xml2'], 
+        ['data', 'asdf']]
+
+        XML:
+        <xml a="b"><xml2/>asdf</xml>
+        """
+        outlines = []
+        outlines.append('<?xml version="1.0" encoding="UTF-8"?>')
+        if (type(data) == type([]) and len(data) > 0):
+            if data[0] == 'tag': outlines.append(self._xmltag(data))
+        return outlines
 
-class Meta:
-       "Meta Data Class"
-
-       def __init__(self, doctype, debug=False):
-               self.doctype = doctype
-
-               # Set the debug mode
-               self.debug = debug
-
-               # The generator should always default to the version number
-               self.meta_generator = version()
-               self.meta_title = ''
-               self.meta_subject = ''
-               self.meta_description = ''
-               self.meta_keywords = []
-               self.meta_creator = 'ooolib-python'
-               self.meta_editor = ''
-               self.meta_user1_name = 'Info 1'
-               self.meta_user2_name = 'Info 2'
-               self.meta_user3_name = 'Info 3'
-               self.meta_user4_name = 'Info 4'
-               self.meta_user1_value = ''
-               self.meta_user2_value = ''
-               self.meta_user3_value = ''
-               self.meta_user4_value = ''
-               self.meta_creation_date = self.meta_time()
-
-               # Parser data
-               self.parser_element_list = []
-               self.parser_element = ""
-               self.parser_count = 0
-
-       def set_meta(self, metaname, value):
-               """Set meta data in your document.
-
-               Currently implemented metaname options are as follows:
-               'creator' - The document author
-               """
-               if metaname == 'creator': self.meta_creator = value
-               if metaname == 'editor': self.meta_editor = value
-               if metaname == 'title': self.meta_title = value
-               if metaname == 'subject': self.meta_subject = value
-               if metaname == 'description': self.meta_description = value
-               if metaname == 'user1name': self.meta_user1_name = value
-               if metaname == 'user2name': self.meta_user2_name = value
-               if metaname == 'user3name': self.meta_user3_name = value
-               if metaname == 'user4name': self.meta_user4_name = value
-               if metaname == 'user1value': self.meta_user1_value = value
-               if metaname == 'user2value': self.meta_user2_value = value
-               if metaname == 'user3value': self.meta_user3_value = value
-               if metaname == 'user4value': self.meta_user4_value = value
-               if metaname == 'keyword':
-                       if value not in self.meta_keywords:
-                               self.meta_keywords.append(value)
-
-       def get_meta_value(self, metaname):
-               "Get meta data value for a given metaname."
-
-               if metaname == 'creator': return self.meta_creator
-               if metaname == 'editor': return self.meta_editor
-               if metaname == 'title': return self.meta_title
-               if metaname == 'subject': return self.meta_subject
-               if metaname == 'description': return self.meta_description
-               if metaname == 'user1name': return self.meta_user1_name
-               if metaname == 'user2name': return self.meta_user2_name
-               if metaname == 'user3name': return self.meta_user3_name
-               if metaname == 'user4name': return self.meta_user4_name
-               if metaname == 'user1value': return self.meta_user1_value
-               if metaname == 'user2value': return self.meta_user2_value
-               if metaname == 'user3value': return self.meta_user3_value
-               if metaname == 'user4value': return self.meta_user4_value
-               if metaname == 'keyword': return self.meta_keywords
-
-       def meta_time(self):
-               "Return time string in meta data format"
-               t = time.localtime()
-               stamp = "%04d-%02d-%02dT%02d:%02d:%02d" % (t[0], t[1], t[2], t[3], t[4], t[5])
-               return stamp
-
-       def parse_start_element(self, name, attrs):
-               if self.debug: print '* Start element:', name
-               self.parser_element_list.append(name)
-               self.parser_element = self.parser_element_list[-1]
-
-               # Need the meta name from the user-defined tags
-               if (self.parser_element == "meta:user-defined"):
-                       self.parser_count += 1
-                       # Set user-defined name
-                       self.set_meta("user%dname" % self.parser_count, attrs['meta:name'])
-
-               # Debugging statements
-               if self.debug: print "  List: ", self.parser_element_list
-               if self.debug: print "  Attributes: ", attrs
-
-
-       def parse_end_element(self, name):
-               if self.debug: print '* End element:', name
-               if name != self.parser_element:
-                       print "Tag Mismatch: '%s' != '%s'" % (name, self.parser_element)
-               self.parser_element_list.pop()
-
-               # Readjust parser_element_list and parser_element
-               if (self.parser_element_list):
-                       self.parser_element = self.parser_element_list[-1]
-               else:
-                       self.parser_element = ""
-
-       def parse_char_data(self, data):
-               if self.debug: print "  Character data: ", repr(data)
-
-               # Collect Meta data fields
-               if (self.parser_element == "dc:title"):
-                       self.set_meta("title", data)
-               if (self.parser_element == "dc:description"):
-                       self.set_meta("description", data)
-               if (self.parser_element == "dc:subject"):
-                       self.set_meta("subject", data)
-               if (self.parser_element == "meta:initial-creator"):
-                       self.set_meta("creator", data)
-
-               # Try to maintain the same creation date
-               if (self.parser_element == "meta:creation-date"):
-                       self.meta_creation_date = data
-               
-               # The user defined fields need to be kept track of, parser_count does that
-               if (self.parser_element == "meta:user-defined"):
-                       self.set_meta("user%dvalue" % self.parser_count, data)
-
-       def meta_parse(self, data):
-               "Parse Meta Data from a meta.xml file"
-
-               # Debugging statements
-               if self.debug:
-                       # Sometimes it helps to see the document that was read from
-                       print data
-                       print "\n\n\n"
-
-               # Create parser
-               parser = xml.parsers.expat.ParserCreate()
-               # Set up parser callback functions
-               parser.StartElementHandler = self.parse_start_element
-               parser.EndElementHandler = self.parse_end_element
-               parser.CharacterDataHandler = self.parse_char_data
-
-               # Actually parse the data
-               parser.Parse(data, 1)
-
-       def get_meta(self):
-               "Generate meta.xml file data"
-               self.meta_date = self.meta_time()
-               self.data = ['tag', 'office:document-meta',
-                 ['element', 'xmlns:office', 'urn:oasis:names:tc:opendocument:xmlns:office:1.0'],
-                 ['element', 'xmlns:xlink', 'http://www.w3.org/1999/xlink'],
-                 ['element', 'xmlns:dc', 'http://purl.org/dc/elements/1.1/'],
-                 ['element', 'xmlns:meta', 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0'],
-                 ['element', 'xmlns:ooo', 'http://openoffice.org/2004/office'],
-                 ['element', 'office:version', '1.0'],
-                 ['tag', 'office:meta',
-                   ['tag', 'meta:generator',             # Was: 'OpenOffice.org/2.0$Linux OpenOffice.org_project/680m5$Build-9011'
-                     ['data', self.meta_generator]],     # Generator is set the the ooolib-python version.
-                   ['tag', 'dc:title',
-                     ['data', self.meta_title]],         # This data is the document title
-                   ['tag', 'dc:description',
-                     ['data', self.meta_description]],   # This data is the document description
-                   ['tag', 'dc:subject',
-                     ['data', self.meta_subject]],       # This data is the document subject
-                   ['tag', 'meta:initial-creator',
-                     ['data', self.meta_creator]],       # This data is the document creator
-                   ['tag', 'meta:creation-date',
-                     ['data', self.meta_creation_date]], # This is the original creation date of the document
-                   ['tag', 'dc:creator',
-                     ['data', self.meta_editor]],        # This data is the document editor
-                   ['tag', 'dc:date',
-                     ['data', self.meta_date]],          # This is the last modified date of the document
-                   ['tag', 'dc:language',
-                     ['data', 'en-US']],                 # We will probably always use en-US for language
-                   ['tag', 'meta:editing-cycles',
-                     ['data', '1']],                     # Edit cycles will probably always be 1 for generated documents
-                   ['tag', 'meta:editing-duration',
-                     ['data', 'PT0S']],                  # Editing duration is modified - creation date
-                   ['tag', 'meta:user-defined',
-                     ['element', 'meta:name', self.meta_user1_name],
-                     ['data', self.meta_user1_value]],
-                   ['tag', 'meta:user-defined',
-                     ['element', 'meta:name', self.meta_user2_name],
-                     ['data', self.meta_user2_value]],
-                   ['tag', 'meta:user-defined',
-                     ['element', 'meta:name', self.meta_user3_name],
-                     ['data', self.meta_user3_value]],
-                   ['tag', 'meta:user-defined',
-                     ['element', 'meta:name', self.meta_user4_name],
-                     ['data', self.meta_user4_value]]]]
-#                  ['tagline', 'meta:document-statistic',
-#                    ['element', 'meta:table-count', len(self.sheets)], # len(self.sheets) ?
-#                    ['element', 'meta:cell-count', '15']]]] # Not sure how to keep track
-
-               # Generate content.xml XML data
-               xml = XML()
-               self.lines = xml.convert(self.data)
-               self.filedata = '\n'.join(self.lines)
-               # Return generated data
-               return self.filedata
 
+class Meta:
+    "Meta Data Class"
+
+    def __init__(self, doctype, debug=False):
+        self.doctype = doctype
+        # Set the debug mode
+        self.debug = debug
+        # The generator should always default to the version number
+        self.meta_generator = version()
+        self.meta_title = ''
+        self.meta_subject = ''
+        self.meta_description = ''
+        self.meta_keywords = []
+        self.meta_creator = 'ooolib-python'
+        self.meta_editor = ''
+        self.meta_user1_name = 'Info 1'
+        self.meta_user2_name = 'Info 2'
+        self.meta_user3_name = 'Info 3'
+        self.meta_user4_name = 'Info 4'
+        self.meta_user1_value = ''
+        self.meta_user2_value = ''
+        self.meta_user3_value = ''
+        self.meta_user4_value = ''
+        self.meta_creation_date = self.meta_time()
+        # Parser data
+        self.parser_element_list = []
+        self.parser_element = ""
+        self.parser_count = 0
+
+    def set_meta(self, metaname, value):
+        """Set meta data in your document.
+
+        Currently implemented metaname options are as follows:
+        'creator' - The document author
+        """
+        if metaname == 'creator': self.meta_creator = value
+        if metaname == 'editor': self.meta_editor = value
+        if metaname == 'title': self.meta_title = value
+        if metaname == 'subject': self.meta_subject = value
+        if metaname == 'description': self.meta_description = value
+        if metaname == 'user1name': self.meta_user1_name = value
+        if metaname == 'user2name': self.meta_user2_name = value
+        if metaname == 'user3name': self.meta_user3_name = value
+        if metaname == 'user4name': self.meta_user4_name = value
+        if metaname == 'user1value': self.meta_user1_value = value
+        if metaname == 'user2value': self.meta_user2_value = value
+        if metaname == 'user3value': self.meta_user3_value = value
+        if metaname == 'user4value': self.meta_user4_value = value
+        if metaname == 'keyword':
+            if value not in self.meta_keywords:
+                self.meta_keywords.append(value)
+
+    def get_meta_value(self, metaname):
+        "Get meta data value for a given metaname."
+        if metaname == 'creator': return self.meta_creator
+        if metaname == 'editor': return self.meta_editor
+        if metaname == 'title': return self.meta_title
+        if metaname == 'subject': return self.meta_subject
+        if metaname == 'description': return self.meta_description
+        if metaname == 'user1name': return self.meta_user1_name
+        if metaname == 'user2name': return self.meta_user2_name
+        if metaname == 'user3name': return self.meta_user3_name
+        if metaname == 'user4name': return self.meta_user4_name
+        if metaname == 'user1value': return self.meta_user1_value
+        if metaname == 'user2value': return self.meta_user2_value
+        if metaname == 'user3value': return self.meta_user3_value
+        if metaname == 'user4value': return self.meta_user4_value
+        if metaname == 'keyword': return self.meta_keywords
+
+    def meta_time(self):
+        "Return time string in meta data format"
+        t = time.localtime()
+        stamp = "%04d-%02d-%02dT%02d:%02d:%02d" % (t[0], t[1], t[2], t[3], t[4], t[5])
+        return stamp
+
+    def parse_start_element(self, name, attrs):
+        if self.debug: print('* Start element:', name)
+        self.parser_element_list.append(name)
+        self.parser_element = self.parser_element_list[-1]
+        # Need the meta name from the user-defined tags
+        if (self.parser_element == "meta:user-defined"):
+            self.parser_count += 1
+            # Set user-defined name
+            self.set_meta("user%dname" % self.parser_count, attrs['meta:name'])
+        # Debugging statements
+        if self.debug: print("  List: ", self.parser_element_list)
+        if self.debug: print("  Attributes: ", attrs)
+
+    def parse_end_element(self, name):
+        if self.debug: print('* End element:', name)
+        if name != self.parser_element:
+            print("Tag Mismatch: '%s' != '%s'" % (name, self.parser_element))
+        self.parser_element_list.pop()
+        # Readjust parser_element_list and parser_element
+        if (self.parser_element_list):
+            self.parser_element = self.parser_element_list[-1]
+        else:
+            self.parser_element = ""
+
+    def parse_char_data(self, data):
+        if self.debug:
+            print("  Character data: ", repr(data))
+        # Collect Meta data fields
+        if (self.parser_element == "dc:title"):
+            self.set_meta("title", data)
+        if (self.parser_element == "dc:description"):
+            self.set_meta("description", data)
+        if (self.parser_element == "dc:subject"):
+            self.set_meta("subject", data)
+        if (self.parser_element == "meta:initial-creator"):
+            self.set_meta("creator", data)
+        # Try to maintain the same creation date
+        if (self.parser_element == "meta:creation-date"):
+            self.meta_creation_date = data
+        # The user defined fields need to be kept track of, parser_count does that
+        if (self.parser_element == "meta:user-defined"):
+            self.set_meta("user%dvalue" % self.parser_count, data)
+
+    def meta_parse(self, data):
+        "Parse Meta Data from a meta.xml file"
+        # Debugging statements
+        if self.debug:
+            # Sometimes it helps to see the document that was read from
+            print(data)
+            print("\n\n\n")
+        # Create parser
+        parser = xml.parsers.expat.ParserCreate()
+        # Set up parser callback functions
+        parser.StartElementHandler = self.parse_start_element
+        parser.EndElementHandler = self.parse_end_element
+        parser.CharacterDataHandler = self.parse_char_data
+        # Actually parse the data
+        parser.Parse(data, 1)
+
+    def get_meta(self):
+        "Generate meta.xml file data"
+        self.meta_date = self.meta_time()
+        self.data = ['tag', 'office:document-meta',
+          ['element', 'xmlns:office', 'urn:oasis:names:tc:opendocument:xmlns:office:1.0'],
+          ['element', 'xmlns:xlink', 'http://www.w3.org/1999/xlink'],
+          ['element', 'xmlns:dc', 'http://purl.org/dc/elements/1.1/'],
+          ['element', 'xmlns:meta', 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0'],
+          ['element', 'xmlns:ooo', 'http://openoffice.org/2004/office'],
+          ['element', 'office:version', '1.0'],
+          ['tag', 'office:meta',
+            ['tag', 'meta:generator',             # Was: 'OpenOffice.org/2.0$Linux OpenOffice.org_project/680m5$Build-9011'
+              ['data', self.meta_generator]],     # Generator is set the the ooolib-python version.
+            ['tag', 'dc:title',
+              ['data', self.meta_title]],         # This data is the document title
+            ['tag', 'dc:description',
+              ['data', self.meta_description]],   # This data is the document description
+            ['tag', 'dc:subject',
+              ['data', self.meta_subject]],       # This data is the document subject
+            ['tag', 'meta:initial-creator',
+              ['data', self.meta_creator]],       # This data is the document creator
+            ['tag', 'meta:creation-date',
+              ['data', self.meta_creation_date]], # This is the original creation date of the document
+            ['tag', 'dc:creator',
+              ['data', self.meta_editor]],        # This data is the document editor
+            ['tag', 'dc:date',
+              ['data', self.meta_date]],          # This is the last modified date of the document
+            ['tag', 'dc:language',
+              ['data', 'en-US']],                 # We will probably always use en-US for language
+            ['tag', 'meta:editing-cycles',
+              ['data', '1']],                     # Edit cycles will probably always be 1 for generated documents
+            ['tag', 'meta:editing-duration',
+              ['data', 'PT0S']],                  # Editing duration is modified - creation date
+            ['tag', 'meta:user-defined',
+              ['element', 'meta:name', self.meta_user1_name],
+              ['data', self.meta_user1_value]],
+            ['tag', 'meta:user-defined',
+              ['element', 'meta:name', self.meta_user2_name],
+              ['data', self.meta_user2_value]],
+            ['tag', 'meta:user-defined',
+              ['element', 'meta:name', self.meta_user3_name],
+              ['data', self.meta_user3_value]],
+            ['tag', 'meta:user-defined',
+              ['element', 'meta:name', self.meta_user4_name],
+              ['data', self.meta_user4_value]]]]
+#            ['tagline', 'meta:document-statistic',
+#              ['element', 'meta:table-count', len(self.sheets)], # len(self.sheets) ?
+#              ['element', 'meta:cell-count', '15']]]] # Not sure how to keep track
+        # Generate content.xml XML data
+        xml = XML()
+        self.lines = xml.convert(self.data)
+        self.filedata = '\n'.join(self.lines)
+        # Return generated data
+        return self.filedata
 
 
 class CalcStyles:
-       "Calc Style Management - Used to keep track of created styles."
-
-       def __init__(self):
-               self.style_config = {}
-               # Style Counters
-               self.style_table = 1
-               self.style_column = 1
-               self.style_row = 1
-               self.style_cell = 1
-               # Style Properties (Defaults) - To be used later
-               self.property_column_width_default = '0.8925in' # Default Column Width
-               self.property_row_height_default = '0.189in'    # Default Row Height
-               # Set Defaults
-               self.property_column_width = '0.8925in' # Default Column Width
-               self.property_row_height = '0.189in'    # Default Row Height
-               self.property_cell_bold = False         # Bold off be default
-               self.property_cell_italic = False       # Italic off be default
-               self.property_cell_underline = False    # Underline off be default
-               self.property_cell_fg_color = 'default' # Text Color Default
-               self.property_cell_bg_color = 'default' # Cell Background Default
-               self.property_cell_bg_image = 'none'    # Cell Background Default
-               self.property_cell_fontsize = '10'      # Cell Font Size Default
-               self.property_cell_valign = 'default'   # Vertial Alignment Default
-               self.property_cell_halign = 'default'   # Horizantal Alignment Default
-
-       def get_next_style(self, style):
-               "Returns the next style code for the given style"
-               style_code = ""
-               if style == 'table':
-                       style_code = 'ta%d' % self.style_table
-                       self.style_table+=1
-               if style == 'column':
-                       style_code = 'co%d' % self.style_column
-                       self.style_column+=1
-               if style == 'row':
-                       style_code = 'ro%d' % self.style_row
-                       self.style_row+=1
-               if style == 'cell':
-                       style_code = 'ce%d' % self.style_cell
-                       self.style_cell+=1
-               return style_code
-
-       def set_property(self, style, name, value):
-               "Sets a property which will later be turned into a code"
-               if style == 'table':
-                       pass
-               if style == 'column':
-                       if name == 'style:column-width': self.property_column_width = value
-               if style == 'row':
-                       if name == 'style:row-height': self.property_row_height = value
-               if style == 'cell':
-                       if name == 'bold' and type(value) == type(True): self.property_cell_bold = value
-                       if name == 'italic' and type(value) == type(True): self.property_cell_italic = value
-                       if name == 'underline' and type(value) == type(True): self.property_cell_underline = value
-                       if name == 'fontsize': self.property_cell_fontsize = value
-                       if name == 'color':
-                               self.property_cell_fg_color = 'default'
-                               redata = re.search("^(#[\da-fA-F]{6})$", value)
-                               if redata: self.property_cell_fg_color = value.lower()
-                       if name == 'background':
-                               self.property_cell_bg_color = 'default'
-                               redata = re.search("^(#[\da-fA-F]{6})$", value)
-                               if redata: self.property_cell_bg_color = value.lower()
-                       if name == 'backgroundimage':
-                               self.property_cell_bg_image = value
-                       if name == 'valign':
-                               self.property_cell_valign = value
-                       if name == 'halign':
-                               self.property_cell_halign = value
-
-       def get_style_code(self, style):
-               style_code = ""
-               if style == 'table':
-                       style_code = "ta1"
-               if style == 'column':
-                       style_data = tuple([style,
-                               ('style:column-width', self.property_column_width)])
-                       if style_data in self.style_config:
-                               # Style Exists, return code
-                               style_code = self.style_config[style_data]
-                       else:
-                               # Style does not exist, create code and return it
-                               style_code = self.get_next_style(style)
-                               self.style_config[style_data] = style_code
-               if style == 'row':
-                       style_data = tuple([style,
-                               ('style:row-height', self.property_row_height)])
-                       if style_data in self.style_config:
-                               # Style Exists, return code
-                               style_code = self.style_config[style_data]
-                       else:
-                               # Style does not exist, create code and return it
-                               style_code = self.get_next_style(style)
-                               self.style_config[style_data] = style_code
-               if style == 'cell':
-                       style_data = [style]
-                       # Add additional styles
-                       if self.property_cell_bold: style_data.append(('bold', True))
-                       if self.property_cell_italic: style_data.append(('italic', True))
-                       if self.property_cell_underline: style_data.append(('underline', True))
-                       if self.property_cell_fontsize != '10':
-                               style_data.append(('fontsize', self.property_cell_fontsize))
-                       if self.property_cell_fg_color != 'default':
-                               style_data.append(('color', self.property_cell_fg_color))
-                       if self.property_cell_bg_color != 'default':
-                               style_data.append(('background', self.property_cell_bg_color))
-                       if self.property_cell_bg_image != 'none':
-                               style_data.append(('backgroundimage', self.property_cell_bg_image))
-                       if self.property_cell_valign != 'default':
-                               style_data.append(('valign', self.property_cell_valign))
-                       if self.property_cell_halign != 'default':
-                               style_data.append(('halign', self.property_cell_halign))
-
-                       style_data = tuple(style_data)
-                       if style_data in self.style_config:
-                               # Style Exists, return code
-                               style_code = self.style_config[style_data]
-                       else:
-                               # Style does not exist, create code and return it
-                               style_code = self.get_next_style(style)
-                               self.style_config[style_data] = style_code
-               return style_code
-
-       def get_automatic_styles(self):
-               "Return 'office:automatic-styles' lists"
-               automatic_styles = ['tag', 'office:automatic-styles']
-
-               for style_data in self.style_config:
-                       style_code = self.style_config[style_data]
-                       style_data = list(style_data)
-                       style = style_data.pop(0)
-
-                       if style == 'column':
-                               style_list = ['tag', 'style:style',
-                                 ['element', 'style:name', style_code],            # Column 'co1' properties
-                                 ['element', 'style:family', 'table-column']]
-                               tagline = ['tagline', 'style:table-column-properties',
-                                 ['element', 'fo:break-before', 'auto']]      # unsure what break before means
-
-                               for set in style_data:
-                                       name, value = set
-                                       if name == 'style:column-width':
-                                               tagline.append(['element', 'style:column-width', value])
-                               style_list.append(tagline)
-                               automatic_styles.append(style_list)
-
-                       if style == 'row':
-                               style_list = ['tag', 'style:style',
-                                 ['element', 'style:name', style_code],            # Column 'ro1' properties
-                                 ['element', 'style:family', 'table-row']]
-                               tagline = ['tagline', 'style:table-row-properties']
-
-                               for set in style_data:
-                                       name, value = set
-                                       if name == 'style:row-height':
-                                               tagline.append(['element', 'style:row-height', value])
-                               tagline.append(['element', 'fo:break-before', 'auto'])
-#                              tagline.append(['element', 'style:use-optimal-row-height', 'true']) # Overrides settings
-                               style_list.append(tagline)
-                               automatic_styles.append(style_list)
-
-                       if style == 'cell':
-                               style_list = ['tag', 'style:style',
-                                 ['element', 'style:name', style_code],             # ce1 style
-                                 ['element', 'style:family', 'table-cell'],         # cell
-                                 ['element', 'style:parent-style-name', 'Default']] # parent is Default
-
-                               # Cell Properties
-                               tagline = ['tag', 'style:table-cell-properties']
-                               tagline_additional = []
-                               for set in style_data:
-                                       name, value = set
-                                       if name == 'background':
-                                               tagline.append(['element', 'fo:background-color', value])
-                                       if name == 'backgroundimage':
-                                               tagline.append(['element', 'fo:background-color', 'transparent'])
-                                               # Additional tags added later
-                                               bgimagetag = ['tagline', 'style:background-image']
-                                               bgimagetag.append(['element', 'xlink:href', value])
-                                               bgimagetag.append(['element', 'xlink:type', 'simple'])
-                                               bgimagetag.append(['element', 'xlink:actuate', 'onLoad'])
-                                               tagline_additional.append(bgimagetag)
-                                       if name == 'valign':
-                                               if value in ['top', 'bottom', 'middle']: 
-                                                       tagline.append(['element', 'style:vertical-align', value])
-                                       if name == 'halign':
-                                               tagline.append(['element', 'style:text-align-source', 'fix'])
-                                               if value in ['filled']:
-                                                       tagline.append(['element', 'style:repeat-content', 'true'])
-                                               else:
-                                                       tagline.append(['element', 'style:repeat-content', 'false'])
-
-                               # Add any additional internal tags
-                               while tagline_additional:
-                                       tagadd = tagline_additional.pop(0)
-                                       tagline.append(tagadd)
-
-                               style_list.append(tagline)
-
-                               # Paragraph Properties
-                               tagline = ['tagline', 'style:paragraph-properties']
-                               tagline_valid = False
-                               for set in style_data:
-                                       name, value = set
-                                       if name == 'halign':
-                                               tagline_valid = True
-                                               if value in ['center']:
-                                                       tagline.append(['element', 'fo:text-align', 'center'])
-                                               if value in ['end', 'right']:
-                                                       tagline.append(['element', 'fo:text-align', 'end'])
-                                               if value in ['start', 'filled', 'left']:
-                                                       tagline.append(['element', 'fo:text-align', 'start'])
-                                               if value in ['justify']:
-                                                       tagline.append(['element', 'fo:text-align', 'justify'])
-                               # Conditionally add the tagline
-                               if tagline_valid: style_list.append(tagline)
-
-
-                               # Text Properties
-                               tagline = ['tagline', 'style:text-properties']
-                               for set in style_data:
-                                       name, value = set
-                                       if name == 'bold':
-                                               tagline.append(['element', 'fo:font-weight', 'bold'])
-                                       if name == 'italic':
-                                               tagline.append(['element', 'fo:font-style', 'italic'])
-                                       if name == 'underline':
-                                               tagline.append(['element', 'style:text-underline-style', 'solid'])
-                                               tagline.append(['element', 'style:text-underline-width', 'auto'])
-                                               tagline.append(['element', 'style:text-underline-color', 'font-color'])
-                                       if name == 'color':
-                                               tagline.append(['element', 'fo:color', value])
-                                       if name == 'fontsize':
-                                               tagline.append(['element', 'fo:font-size', '%spt' % value])
-                               style_list.append(tagline)
-
-                               automatic_styles.append(style_list)
-                               
-
-               # Attach ta1 style
-               automatic_styles.append(['tag', 'style:style',
-                 ['element', 'style:name', 'ta1'],
-                 ['element', 'style:family', 'table'],
-                 ['element', 'style:master-page-name', 'Default'],
-                 ['tagline', 'style:table-properties',
-                   ['element', 'table:display', 'true'],
-                   ['element', 'style:writing-mode', 'lr-tb']]])
-
-
-               return automatic_styles
-
+    "Calc Style Management - Used to keep track of created styles."
+
+    def __init__(self):
+        self.style_config = {}
+        # Style Counters
+        self.style_table = 1
+        self.style_column = 1
+        self.style_row = 1
+        self.style_cell = 1
+        # Style Properties (Defaults) - To be used later
+        self.property_column_width_default = '0.8925in' # Default Column Width
+        self.property_row_height_default = '0.189in'    # Default Row Height
+        # Set Defaults
+        self.property_column_width = '0.8925in' # Default Column Width
+        self.property_row_height = '0.189in'    # Default Row Height
+        self.property_cell_bold = False         # Bold off be default
+        self.property_cell_italic = False       # Italic off be default
+        self.property_cell_underline = False    # Underline off be default
+        self.property_cell_fg_color = 'default' # Text Color Default
+        self.property_cell_bg_color = 'default' # Cell Background Default
+        self.property_cell_bg_image = 'none'    # Cell Background Default
+        self.property_cell_fontsize = '10'      # Cell Font Size Default
+        self.property_cell_valign = 'default'    # Vertial Alignment Default
+        self.property_cell_halign = 'default'    # Horizantal Alignment Default
+
+    def get_next_style(self, style):
+        "Returns the next style code for the given style"
+        style_code = ""
+        if style == 'table':
+            style_code = 'ta%d' % self.style_table
+            self.style_table+=1
+        if style == 'column':
+            style_code = 'co%d' % self.style_column
+            self.style_column+=1
+        if style == 'row':
+            style_code = 'ro%d' % self.style_row
+            self.style_row+=1
+        if style == 'cell':
+            style_code = 'ce%d' % self.style_cell
+            self.style_cell+=1
+        return style_code
+
+    def set_property(self, style, name, value):
+        "Sets a property which will later be turned into a code"
+        if style == 'table':
+            pass
+        if style == 'column':
+            if name == 'style:column-width': self.property_column_width = value
+        if style == 'row':
+            if name == 'style:row-height': self.property_row_height = value
+        if style == 'cell':
+            if name == 'bold' and type(value) == type(True): self.property_cell_bold = value
+            if name == 'italic' and type(value) == type(True): self.property_cell_italic = value
+            if name == 'underline' and type(value) == type(True): self.property_cell_underline = value
+            if name == 'fontsize': self.property_cell_fontsize = value
+            if name == 'color':
+                self.property_cell_fg_color = 'default'
+                redata = re.search("^(#[\da-fA-F]{6})$", value)
+                if redata: self.property_cell_fg_color = value.lower()
+            if name == 'background':
+                self.property_cell_bg_color = 'default'
+                redata = re.search("^(#[\da-fA-F]{6})$", value)
+                if redata: self.property_cell_bg_color = value.lower()
+            if name == 'backgroundimage':
+                self.property_cell_bg_image = value
+            if name == 'valign':
+                self.property_cell_valign = value
+            if name == 'halign':
+                self.property_cell_halign = value
+
+    def get_style_code(self, style):
+        style_code = ""
+        if style == 'table':
+            style_code = "ta1"
+        if style == 'column':
+            style_data = tuple([style,
+                ('style:column-width', self.property_column_width)])
+            if style_data in self.style_config:
+                # Style Exists, return code
+                style_code = self.style_config[style_data]
+            else:
+                # Style does not exist, create code and return it
+                style_code = self.get_next_style(style)
+                self.style_config[style_data] = style_code
+        if style == 'row':
+            style_data = tuple([style,
+                ('style:row-height', self.property_row_height)])
+            if style_data in self.style_config:
+                # Style Exists, return code
+                style_code = self.style_config[style_data]
+            else:
+                # Style does not exist, create code and return it
+                style_code = self.get_next_style(style)
+                self.style_config[style_data] = style_code
+        if style == 'cell':
+            style_data = [style]
+            # Add additional styles
+            if self.property_cell_bold: style_data.append(('bold', True))
+            if self.property_cell_italic: style_data.append(('italic', True))
+            if self.property_cell_underline: style_data.append(('underline', True))
+            if self.property_cell_fontsize != '10':
+                style_data.append(('fontsize', self.property_cell_fontsize))
+            if self.property_cell_fg_color != 'default':
+                style_data.append(('color', self.property_cell_fg_color))
+            if self.property_cell_bg_color != 'default':
+                style_data.append(('background', self.property_cell_bg_color))
+            if self.property_cell_bg_image != 'none':
+                style_data.append(('backgroundimage', self.property_cell_bg_image))
+            if self.property_cell_valign != 'default':
+                style_data.append(('valign', self.property_cell_valign))
+            if self.property_cell_halign != 'default':
+                style_data.append(('halign', self.property_cell_halign))
+            style_data = tuple(style_data)
+            if style_data in self.style_config:
+                # Style Exists, return code
+                style_code = self.style_config[style_data]
+            else:
+                # Style does not exist, create code and return it
+                style_code = self.get_next_style(style)
+                self.style_config[style_data] = style_code
+        return style_code
+
+    def get_automatic_styles(self):
+        "Return 'office:automatic-styles' lists"
+        automatic_styles = ['tag', 'office:automatic-styles']
+        for style_data in self.style_config:
+            style_code = self.style_config[style_data]
+            style_data = list(style_data)
+            style = style_data.pop(0)
+            if style == 'column':
+                style_list = ['tag', 'style:style',
+                  ['element', 'style:name', style_code],            # Column 'co1' properties
+                  ['element', 'style:family', 'table-column']]
+                tagline = ['tagline', 'style:table-column-properties',
+                  ['element', 'fo:break-before', 'auto']]      # unsure what break before means
+                for set in style_data:
+                    name, value = set
+                    if name == 'style:column-width':
+                        tagline.append(['element', 'style:column-width', value])
+                style_list.append(tagline)
+                automatic_styles.append(style_list)
+            if style == 'row':
+                style_list = ['tag', 'style:style',
+                  ['element', 'style:name', style_code],            # Column 'ro1' properties
+                  ['element', 'style:family', 'table-row']]
+                tagline = ['tagline', 'style:table-row-properties']
+                for set in style_data:
+                    name, value = set
+                    if name == 'style:row-height':
+                        tagline.append(['element', 'style:row-height', value])
+                tagline.append(['element', 'fo:break-before', 'auto'])
+#                tagline.append(['element', 'style:use-optimal-row-height', 'true']) # Overrides settings
+                style_list.append(tagline)
+                automatic_styles.append(style_list)
+            if style == 'cell':
+                style_list = ['tag', 'style:style',
+                    ['element', 'style:name', style_code],             # ce1 style
+                  ['element', 'style:family', 'table-cell'],         # cell
+                  ['element', 'style:parent-style-name', 'Default']] # parent is Default
+                # Cell Properties
+                tagline = ['tag', 'style:table-cell-properties']
+                tagline_additional = []
+                for set in style_data:
+                    name, value = set
+                    if name == 'background':
+                        tagline.append(['element', 'fo:background-color', value])
+                    if name == 'backgroundimage':
+                        tagline.append(['element', 'fo:background-color', 'transparent'])
+                        # Additional tags added later
+                        bgimagetag = ['tagline', 'style:background-image']
+                        bgimagetag.append(['element', 'xlink:href', value])
+                        bgimagetag.append(['element', 'xlink:type', 'simple'])
+                        bgimagetag.append(['element', 'xlink:actuate', 'onLoad'])
+                        tagline_additional.append(bgimagetag)
+                    if name == 'valign':
+                        if value in ['top', 'bottom', 'middle']: 
+                            tagline.append(['element', 'style:vertical-align', value])
+                    if name == 'halign':
+                        tagline.append(['element', 'style:text-align-source', 'fix'])
+                        if value in ['filled']:
+                            tagline.append(['element', 'style:repeat-content', 'true'])
+                        else:
+                            tagline.append(['element', 'style:repeat-content', 'false'])
+                # Add any additional internal tags
+                while tagline_additional:
+                    tagadd = tagline_additional.pop(0)
+                    tagline.append(tagadd)
+                style_list.append(tagline)
+                # Paragraph Properties
+                tagline = ['tagline', 'style:paragraph-properties']
+                tagline_valid = False
+                for set in style_data:
+                    name, value = set
+                    if name == 'halign':
+                        tagline_valid = True
+                        if value in ['center']:
+                            tagline.append(['element', 'fo:text-align', 'center'])
+                        if value in ['end', 'right']:
+                            tagline.append(['element', 'fo:text-align', 'end'])
+                        if value in ['start', 'filled', 'left']:
+                            tagline.append(['element', 'fo:text-align', 'start'])
+                        if value in ['justify']:
+                            tagline.append(['element', 'fo:text-align', 'justify'])
+                # Conditionally add the tagline
+                if tagline_valid:
+                    style_list.append(tagline)
+                # Text Properties
+                tagline = ['tagline', 'style:text-properties']
+                for set in style_data:
+                    name, value = set
+                    if name == 'bold':
+                        tagline.append(['element', 'fo:font-weight', 'bold'])
+                    if name == 'italic':
+                        tagline.append(['element', 'fo:font-style', 'italic'])
+                    if name == 'underline':
+                        tagline.append(['element', 'style:text-underline-style', 'solid'])
+                        tagline.append(['element', 'style:text-underline-width', 'auto'])
+                        tagline.append(['element', 'style:text-underline-color', 'font-color'])
+                    if name == 'color':
+                        tagline.append(['element', 'fo:color', value])
+                    if name == 'fontsize':
+                        tagline.append(['element', 'fo:font-size', '%spt' % value])
+                style_list.append(tagline)
+                automatic_styles.append(style_list)
+        # Attach ta1 style
+        automatic_styles.append(['tag', 'style:style',
+          ['element', 'style:name', 'ta1'],
+          ['element', 'style:family', 'table'],
+          ['element', 'style:master-page-name', 'Default'],
+          ['tagline', 'style:table-properties',
+            ['element', 'table:display', 'true'],
+            ['element', 'style:writing-mode', 'lr-tb']]])
+        return automatic_styles
 
 
 class CalcSheet:
-       "Calc Sheet Class - Used to keep track of the data for an individual sheet."
-
-       def __init__(self, sheetname):
-               "Initialize a sheet"
-               self.sheet_name = sheetname
-               self.sheet_values = {}
-               self.sheet_config = {}
-               self.max_col = 0
-               self.max_row = 0
-
-       def get_sheet_dimensions(self):
-               "Returns the max column and row"
-               return (self.max_col, self.max_row)
-
-       def clean_formula(self, data):
-               "Returns a formula for use in ODF"
-               # Example Translations
-               # '=SUM(A1:A2)'
-               # datavalue = 'oooc:=SUM([.A1:.A2])'
-               # '=IF((A5>A4);A4;"")'
-               # datavalue = 'oooc:=IF(([.A5]&gt;[.A4]);[.A4];&quot;&quot;)'
-               data = str(data)
-               data = clean_string(data)
-               redata = re.search('^=([A-Z]+)(\(.*)$', data)
-               if redata:
-                       # funct is the function name.  The rest if the string will be the functArgs
-                       funct = redata.group(1)
-                       functArgs = redata.group(2)
-                       # Search for cell lebels and replace them
-                       reList = re.findall('([A-Z]+\d+)', functArgs)
-                       # sort and keep track so we do not do a cell more than once
-                       reList.sort()
-                       lastVar = ''
-                       while reList:
-                               # Replace each cell label
-                               curVar = reList.pop()
-                               if curVar == lastVar: continue
-                               lastVar = curVar
-                               functArgs = functArgs.replace(curVar, '[.%s]' % curVar)
-                       data = 'oooc:=%s%s' % (funct, functArgs)
-               return data
-
-       def get_name(self):
-               "Returns the sheet name"
-               return self.sheet_name
-
-       def set_name(self, sheetname):
-               "Resets the sheet name"
-               self.sheet_name = sheetname
-
-       def get_sheet_values(self):
-               "Returns the sheet cell values"
-               return self.sheet_values
-
-       def get_sheet_value(self, col, row):
-               "Get the value contents of a cell"
-               cell = (col, row)
-               if cell in self.sheet_values:
-                       return self.sheet_values[cell]
-               else:
-                       return None
-
-       def get_sheet_config(self):
-               "Returns the sheet cell properties"
-               return self.sheet_config
-
-       def set_sheet_config(self, location, style_code):
-               "Sets Style Code for a given location"
-               self.sheet_config[location] = style_code
-
-       def set_sheet_value(self, cell, datatype, datavalue):
-               """Sets the value for a specific cell
-
-               cell must be in the format (col, row) where row and col are int.
-               Example: B5 would be written as (2, 5)
-               datatype must be one of 'string', 'float', 'formula'
-               datavalue should be a string
-               """
-               # Catch invalid data
-               if type(cell) != type(()) or len(cell) != 2:
-                       print "Invalid Cell"
-                       return
-               (col, row) = cell
-               if type(col) != type(1):
-                       print "Invalid Cell"
-                       return
-               if type(row) != type(1):
-                       print "Invalid Cell"
-                       return
-               # Fix String Data
-               if datatype in ['string', 'annotation']:
-                       datavalue = clean_string(datavalue)
-               # Fix Link Data. Link's value is a tuple containing (url, description)
-               if (datatype == 'link'):
-                       url = clean_string(datavalue[0])
-                       desc = clean_string(datavalue[1])
-                       datavalue = (url, desc)
-               # Fix Formula Data
-               if datatype == 'formula':
-                       datavalue = self.clean_formula(datavalue)
-               # Adjust maximum sizes
-               if col > self.max_col: self.max_col = col
-               if row > self.max_row: self.max_row = row
-               datatype = str(datatype)
-               if (datatype not in ['string', 'float', 'formula', 'annotation', 'link']):
-                       # Set all unknown cell types to string
-                       datatype = 'string'
-                       datavalue = str(datavalue)
-
-               # The following lines are taken directly from HPS
-               # self.sheet_values[cell] = (datatype, datavalue)
-               # HPS: Cell content is now a list of tuples instead of a tuple
-               # While storing here, store the cell contents first and the annotation next. While generating the XML reverse this
-               contents = self.sheet_values.get(cell, {'annotation':None,'link':None, 'value':None})
-               if datatype == 'annotation':
-                       contents['annotation'] = (datatype, datavalue)
-               elif datatype == 'link':
-                       contents['link'] = (datatype, datavalue)
-               else:
-                       contents['value'] = (datatype, datavalue)
-
-               self.sheet_values[cell] = contents
-
-
-       def get_lists(self):
-               "Returns nested lists for XML processing"
-               if (self.max_col == 0 and self.max_row == 0):
-                       sheet_lists = ['tag', 'table:table',
-                         ['element', 'table:name', self.sheet_name], # Set the Sheet Name
-                         ['element', 'table:style-name', 'ta1'],
-                         ['element', 'table:print', 'false'],
-                         ['tagline', 'table:table-column',
-                           ['element', 'table:style-name', 'co1'],
-                           ['element', 'table:default-cell-style-name', 'Default']],
-                           ['tag', 'table:table-row',
-                             ['element', 'table:style-name', 'ro1'],
-                             ['tagline', 'table:table-cell']]]
-               else:
-                       # Base Information
-                       sheet_lists = ['tag', 'table:table',
-                         ['element', 'table:name', self.sheet_name], # Set the sheet name
-                         ['element', 'table:style-name', 'ta1'],
-                         ['element', 'table:print', 'false']]
-
-#                        ['tagline', 'table:table-column',
-#                          ['element', 'table:style-name', 'co1'],
-#                          ['element', 'table:number-columns-repeated', self.max_col], # max_col? '2'
-#                          ['element', 'table:default-cell-style-name', 'Default']],
-
-                       # Need to add column information                        
-                       for col in range(1, self.max_col+1):
-                               location = ('col', col)
-                               style_code = 'co1'
-                               if location in self.sheet_config:
-                                       style_code = self.sheet_config[location]
-                               sheet_lists.append(['tagline', 'table:table-column',
-                                 ['element', 'table:style-name', style_code],
-                                 ['element', 'table:default-cell-style-name', 'Default']])
-
-
-                       # Need to create each row
-                       for row in range(1, self.max_row + 1):
-                               location = ('row', row)
-                               style_code = 'ro1'
-                               if location in self.sheet_config:
-                                       style_code = self.sheet_config[location]                                
-                               rowlist = ['tag', 'table:table-row',
-                                 ['element', 'table:style-name', style_code]]
-                               for col in range(1, self.max_col + 1):
-                                       cell = (col, row)
-                                       style_code = 'ce1'                           # Default all cells to ce1
-                                       if cell in self.sheet_config:
-                                               style_code = self.sheet_config[cell] # Lookup cell if available
-                                       if cell in self.sheet_values:
-                                               # (datatype, datavalue) = self.sheet_values[cell] # Marked for removal
-                                               collist = ['tag', 'table:table-cell']
-                                               if style_code != 'ce1':
-                                                       collist.append(['element', 'table:style-name', style_code])
-
-                                               # Contents, annotations, and links added by HPS
-                                               contents = self.sheet_values[cell] # cell contents is a dictionary
-                                               if contents['value']:
-                                                       (datatype, datavalue) = contents['value']
-                                                       if datatype == 'float':
-                                                               collist.append(['element', 'office:value-type', datatype])
-                                                               collist.append(['element', 'office:value', datavalue])
-
-                                                       if datatype == 'string':
-                                                               collist.append(['element', 'office:value-type', datatype])
-                                                       if datatype == 'formula':
-                                                               collist.append(['element', 'table:formula', datavalue])
-                                                               collist.append(['element', 'office:value-type', 'float'])
-                                                               collist.append(['element', 'office:value', '0'])
-                                                               datavalue = '0'
-                                               else:
-                                                       datavalue = None
-
-                                               if contents['annotation']:
-                                                       (annotype, annoval) = contents['annotation']
-                                                       collist.append(['tag', 'office:annotation',
-                                                         ['tag', 'text:p', ['data', annoval]]])
-
-                                               if contents['link']:
-                                                       (linktype, linkval) = contents['link']
-                                                       if datavalue:
-                                                               collist.append(['tag', 'text:p', ['data', datavalue],
-                                                                 ['tag', 'text:a', ['element', 'xlink:href', linkval[0]],
-                                                                 ['data', linkval[1]]]])
-                                                       else: # no value; just fill the link
-                                                               collist.append(['tag', 'text:p',
-                                                                 ['tag', 'text:a', ['element', 'xlink:href', linkval[0]],
-                                                                 ['data', linkval[1]]]])
-                                               else:
-                                                       if datavalue:
-                                                               collist.append(['tag', 'text:p', ['data', datavalue]])
-
-
-
-                                       else:
-                                               collist = ['tagline', 'table:table-cell']
-                                       rowlist.append(collist)
-                               sheet_lists.append(rowlist)
-               return sheet_lists
+    "Calc Sheet Class - Used to keep track of the data for an individual sheet."
+
+    def __init__(self, sheetname):
+        "Initialize a sheet"
+        self.sheet_name = sheetname
+        self.sheet_values = {}
+        self.sheet_config = {}
+        self.max_col = 0
+        self.max_row = 0
+
+    def get_sheet_dimensions(self):
+        "Returns the max column and row"
+        return (self.max_col, self.max_row)
+
+    def clean_formula(self, data):
+        "Returns a formula for use in ODF"
+        # Example Translations
+        # '=SUM(A1:A2)'
+        # datavalue = 'oooc:=SUM([.A1:.A2])'
+        # '=IF((A5>A4);A4;"")'
+        # datavalue = 'oooc:=IF(([.A5]&gt;[.A4]);[.A4];&quot;&quot;)'
+        data = str(data)
+        data = clean_string(data)
+        redata = re.search('^=([A-Z]+)(\(.*)$', data)
+        if redata:
+            # funct is the function name.  The rest if the string will be the functArgs
+            funct = redata.group(1)
+            functArgs = redata.group(2)
+            # Search for cell lebels and replace them
+            reList = re.findall('([A-Z]+\d+)', functArgs)
+            # sort and keep track so we do not do a cell more than once
+            reList.sort()
+            lastVar = ''
+            while reList:
+                # Replace each cell label
+                curVar = reList.pop()
+                if curVar == lastVar: continue
+                lastVar = curVar
+                functArgs = functArgs.replace(curVar, '[.%s]' % curVar)
+            data = 'oooc:=%s%s' % (funct, functArgs)
+        return data
+
+    def get_name(self):
+        "Returns the sheet name"
+        return self.sheet_name
+
+    def set_name(self, sheetname):
+        "Resets the sheet name"
+        self.sheet_name = sheetname
+
+    def get_sheet_values(self):
+        "Returns the sheet cell values"
+        return self.sheet_values
+
+    def get_sheet_value(self, col, row):
+        "Get the value contents of a cell"
+        cell = (col, row)
+        if cell in self.sheet_values:
+            return self.sheet_values[cell]
+        else:
+            return None
+
+    def get_sheet_config(self):
+        "Returns the sheet cell properties"
+        return self.sheet_config
+
+    def set_sheet_config(self, location, style_code):
+        "Sets Style Code for a given location"
+        self.sheet_config[location] = style_code
+
+    def set_sheet_value(self, cell, datatype, datavalue):
+        """Sets the value for a specific cell
+
+        cell must be in the format (col, row) where row and col are int.
+        Example: B5 would be written as (2, 5)
+        datatype must be one of 'string', 'float', 'formula'
+        datavalue should be a string
+        """
+        # Catch invalid data
+        if type(cell) != type(()) or len(cell) != 2:
+            print("Invalid Cell")
+            return
+        (col, row) = cell
+        if type(col) != type(1):
+            print("Invalid Cell")
+            return
+        if type(row) != type(1):
+            print("Invalid Cell")
+            return
+        # Fix String Data
+        if datatype in ['string', 'annotation']:
+            datavalue = clean_string(datavalue)
+        # Fix Link Data. Link's value is a tuple containing (url, description)
+        if (datatype == 'link'):
+            url = clean_string(datavalue[0])
+            desc = clean_string(datavalue[1])
+            datavalue = (url, desc)
+        # Fix Formula Data
+        if datatype == 'formula':
+            datavalue = self.clean_formula(datavalue)
+        # Adjust maximum sizes
+        if col > self.max_col: self.max_col = col
+        if row > self.max_row: self.max_row = row
+        datatype = str(datatype)
+        if (datatype not in ['string', 'float', 'formula', 'annotation', 'link']):
+            # Set all unknown cell types to string
+            datatype = 'string'
+            datavalue = str(datavalue)
+        # The following lines are taken directly from HPS
+        # self.sheet_values[cell] = (datatype, datavalue)
+        # HPS: Cell content is now a list of tuples instead of a tuple
+        # While storing here, store the cell contents first and the annotation next. While generating the XML reverse this
+        contents = self.sheet_values.get(cell, {'annotation':None,'link':None, 'value':None})
+        if datatype == 'annotation':
+            contents['annotation'] = (datatype, datavalue)
+        elif datatype == 'link':
+            contents['link'] = (datatype, datavalue)
+        else:
+            contents['value'] = (datatype, datavalue)
+        self.sheet_values[cell] = contents
+
+    def get_lists(self):
+        "Returns nested lists for XML processing"
+        if (self.max_col == 0 and self.max_row == 0):
+            sheet_lists = ['tag', 'table:table',
+              ['element', 'table:name', self.sheet_name], # Set the Sheet Name
+              ['element', 'table:style-name', 'ta1'],
+              ['element', 'table:print', 'false'],
+              ['tagline', 'table:table-column',
+                ['element', 'table:style-name', 'co1'],
+                ['element', 'table:default-cell-style-name', 'Default']],
+                ['tag', 'table:table-row',
+                  ['element', 'table:style-name', 'ro1'],
+                  ['tagline', 'table:table-cell']]]
+        else:
+            # Base Information
+            sheet_lists = ['tag', 'table:table',
+              ['element', 'table:name', self.sheet_name], # Set the sheet name
+              ['element', 'table:style-name', 'ta1'],
+              ['element', 'table:print', 'false']]
+#              ['tagline', 'table:table-column',
+#                ['element', 'table:style-name', 'co1'],
+#                ['element', 'table:number-columns-repeated', self.max_col], # max_col? '2'
+#                ['element', 'table:default-cell-style-name', 'Default']],
+            # Need to add column information            
+            for col in range(1, self.max_col+1):
+                location = ('col', col)
+                style_code = 'co1'
+                if location in self.sheet_config:
+                    style_code = self.sheet_config[location]
+                sheet_lists.append(['tagline', 'table:table-column',
+                  ['element', 'table:style-name', style_code],
+                  ['element', 'table:default-cell-style-name', 'Default']])
+            # Need to create each row
+            for row in range(1, self.max_row + 1):
+                location = ('row', row)
+                style_code = 'ro1'
+                if location in self.sheet_config:
+                    style_code = self.sheet_config[location]                
+                rowlist = ['tag', 'table:table-row',
+                  ['element', 'table:style-name', style_code]]
+                for col in range(1, self.max_col + 1):
+                    cell = (col, row)
+                    style_code = 'ce1'                           # Default all cells to ce1
+                    if cell in self.sheet_config:
+                        style_code = self.sheet_config[cell] # Lookup cell if available
+                    if cell in self.sheet_values:
+                        # (datatype, datavalue) = self.sheet_values[cell] # Marked for removal
+                        collist = ['tag', 'table:table-cell']
+                        if style_code != 'ce1':
+                            collist.append(['element', 'table:style-name', style_code])
+                        # Contents, annotations, and links added by HPS
+                        contents = self.sheet_values[cell] # cell contents is a dictionary
+                        if contents['value']:
+                            (datatype, datavalue) = contents['value']
+                            if datatype == 'float':
+                                collist.append(['element', 'office:value-type', datatype])
+                                collist.append(['element', 'office:value', datavalue])
+                            if datatype == 'string':
+                                collist.append(['element', 'office:value-type', datatype])
+                            if datatype == 'formula':
+                                collist.append(['element', 'table:formula', datavalue])
+                                collist.append(['element', 'office:value-type', 'float'])
+                                collist.append(['element', 'office:value', '0'])
+                                datavalue = '0'
+                        else:
+                            datavalue = None
+                        if contents['annotation']:
+                            (annotype, annoval) = contents['annotation']
+                            collist.append(['tag', 'office:annotation',
+                              ['tag', 'text:p', ['data', annoval]]])
+                        if contents['link']:
+                            (linktype, linkval) = contents['link']
+                            if datavalue:
+                                collist.append(['tag', 'text:p', ['data', datavalue],
+                                  ['tag', 'text:a', ['element', 'xlink:href', linkval[0]],
+                                  ['data', linkval[1]]]])
+                            else: # no value; just fill the link
+                                collist.append(['tag', 'text:p',
+                                  ['tag', 'text:a', ['element', 'xlink:href', linkval[0]],
+                                  ['data', linkval[1]]]])
+                        else:
+                            if datavalue:
+                                collist.append(['tag', 'text:p', ['data', datavalue]])
+                    else:
+                        collist = ['tagline', 'table:table-cell']
+                    rowlist.append(collist)
+                sheet_lists.append(rowlist)
+        return sheet_lists
+
 
 class Calc:
-       "Calc Class - Used to create OpenDocument Format Calc Spreadsheets."
-       def __init__(self, sheetname=None, opendoc=None, debug=False):
-               "Initialize ooolib Calc instance"
-               # Default to no debugging
-               self.debug = debug
-               if not sheetname: sheetname = "Sheet1"
-               self.sheets = [CalcSheet(sheetname)]  # The main sheet will be initially called 'Sheet1'
-               self.sheet_index = 0                  # We initially start on the first sheet
-               self.styles = CalcStyles()
-               self.meta = Meta('ods')
-               self.styles.get_style_code('column')  # Force generation of default column
-               self.styles.get_style_code('row')     # Force generation of default row
-               self.styles.get_style_code('table')   # Force generation of default table
-               self.styles.get_style_code('cell')    # Force generation of default cell
-               self.manifest_files = []              # List of extra files included
-               self.manifest_index = 1               # Index of added manifest files
-
-               # Data Parsing
-               self.parser_element_list = []
-               self.parser_element = ""
-               self.parser_sheet_num = 0
-               self.parser_sheet_row = 0
-               self.parser_sheet_column = 0
-               self.parser_cell_repeats = 0
-               self.parser_cell_string_pending = False
-               self.parser_cell_string_line = ""
-
-               # See if we need to read a document
-               if opendoc:
-                       # Verify that the document exists
-                       if self.debug: print "Opening Document: %s" % opendoc
-
-                       # Okay, now we load the file
-                       self.load(opendoc)
-
-       def debug_level(self, level):
-               """Set debug level:
-               True if you want debugging messages
-               False if you do not.
-               """
-               self.debug = level
-
-       def file_mimetype(self, filename):
-               "Determine the filetype from the filename"
-               parts = filename.lower().split('.')
-               ext = parts[-1]
-               if (ext == 'png'): return (ext, "image/png")
-               if (ext == 'gif'): return (ext, "image/gif")
-               return (ext, "image/unknown")
-
-       def add_file(self, filename):
-               """Prepare a file for loading into ooolib
-
-               The filename should be the local filesystem name for
-               the file.  The file is then prepared to be included in
-               the creation of the final document.  The file needs to
-               remain in place so that it is available when the actual
-               document creation happens.
-               """
-               # mimetype set to (ext, filetype)
-               mimetype = self.file_mimetype(filename)
-               newname = "Pictures/%08d.%s" % (self.manifest_index, mimetype[0])
-               self.manifest_index += 1
-               filetype = mimetype[1]
-               self.manifest_files.append((filename, filetype, newname))
-               return newname
-
-       def set_meta(self, metaname, value):
-               "Set meta data in your document."
-               self.meta.set_meta(metaname, value)
-
-       def get_meta_value(self, metaname):
-               "Get meta data value for a given metaname"
-               return self.meta.get_meta_value(metaname)
-
-       def get_sheet_name(self):
-               "Returns the sheet name"
-               return self.sheets[self.sheet_index].get_name()
-
-       def get_sheet_dimensions(self):
-               "Returns the sheet dimensions in (cols, rows)"
-               return self.sheets[self.sheet_index].get_sheet_dimensions()
-
-       def set_column_property(self, column, name, value):
-               "Set Column Properties"
-               if name == 'width':
-                       # column number column needs column-width set to value
-                       self.styles.set_property('column', 'style:column-width', value)
-                       style_code = self.styles.get_style_code('column')
-                       self.sheets[self.sheet_index].set_sheet_config(('col', column), style_code)
-
-       def set_row_property(self, row, name, value):
-               "Set row Properties"
-               if name == 'height':
-                       # row number row needs row-height set to value
-                       self.styles.set_property('row', 'style:row-height', value)
-                       style_code = self.styles.get_style_code('row')
-                       self.sheets[self.sheet_index].set_sheet_config(('row', row), style_code)
-
-       def set_cell_property(self, name, value):
-               """Turn and off cell properties
-
-               Actual application of properties is handled by setting a value."""
-               # background images need to be handled a little differently
-               # because they need to also be inserted into the final document
-               if (name == 'backgroundimage'):
-                       # Add file and modify value
-                       value = self.add_file(value)
-               self.styles.set_property('cell', name, value)
-
-       def get_sheet_index(self):
-               "Return the current sheet index number"
-               return self.sheet_index
-
-       def set_sheet_index(self, index):
-               "Set the sheet index"
-               if type(index) == type(1):
-                       if index >= 0 and index < len(self.sheets):
-                               self.sheet_index = index
-               return self.sheet_index
-
-       def get_sheet_count(self):
-               "Returns the number of existing sheets"
-               return len(self.sheets)
-
-       def new_sheet(self, sheetname):
-               "Create a new sheet"
-               self.sheet_index = len(self.sheets)
-               self.sheets.append(CalcSheet(sheetname))
-               return self.sheet_index
-
-       def set_cell_value(self, col, row, datatype, value):
-               "Set the value for a given cell"
-               self.sheets[self.sheet_index].set_sheet_value((col, row), datatype, value)
-               style_code = self.styles.get_style_code('cell')
-               self.sheets[self.sheet_index].set_sheet_config((col, row), style_code)
-
-       def get_cell_value(self, col, row):
-               "Get a cell value tuple (type, value) for a given cell"
-               sheetvalue = self.sheets[self.sheet_index].get_sheet_value(col, row)
-               # We stop here if there is no value for sheetvalue
-               if sheetvalue == None: return sheetvalue
-               # Now check to see if we have a value tuple
-               if 'value' in sheetvalue:
-                       return sheetvalue['value']
-               else:
-                       return None
-
-       def load(self, filename):
-               """Load .ods spreadsheet.
-
-               The load function loads data from a document into the current cells.
-               """
-               # Read in the important files
-
-               # meta.xml
-               data = self._zip_read(filename, "meta.xml")
-               self.meta.meta_parse(data)              
-
-               # content.xml
-               data = self._zip_read(filename, "content.xml")
-               self.content_parse(data)
-
-               # settings.xml - I do not remember putting anything here
-               # styles.xml - I do not remember putting anything here
-
-       def parse_content_start_element(self, name, attrs):
-               if self.debug: print '* Start element:', name
-               self.parser_element_list.append(name)
-               self.parser_element = self.parser_element_list[-1]
-
-               # Keep track of the current sheet number
-               if (self.parser_element == 'table:table'):
-                       # Move to starting cell
-                       self.parser_sheet_row = 0
-                       self.parser_sheet_column = 0
-                       # Increment the sheet number count
-                       self.parser_sheet_num += 1
-                       if (self.parser_sheet_num - 1 != self.sheet_index):
-                               # We are not on the first sheet and need to create a new sheet.
-                               # We will automatically move to the new sheet
-                               sheetname = "Sheet%d" % self.parser_sheet_num
-                               if 'table:name' in attrs: sheetname = attrs['table:name']
-                               self.new_sheet(sheetname)
-                       else:
-                               # We are on the first sheet and will need to overwrite the default name
-                               sheetname = "Sheet%d" % self.parser_sheet_num
-                               if 'table:name' in attrs: sheetname = attrs['table:name']
-                               self.sheets[self.sheet_index].set_name(sheetname)
-
-               # Update the row numbers
-               if (self.parser_element == 'table:table-row'):
-                       self.parser_sheet_row += 1
-                       self.parser_sheet_column = 0
-
-               # Okay, now keep track of the sheet cell data
-               if (self.parser_element == 'table:table-cell'):
-                       # By default it will repeat zero times
-                       self.parser_cell_repeats = 0
-                       # We must be in a new column
-                       self.parser_sheet_column += 1
-                       # Set some default values
-                       datatype = ""
-                       value = ""
-                       # Get values from attrs hash
-                       if 'office:value-type' in attrs: datatype = attrs['office:value-type']
-                       if 'office:value' in attrs: value = attrs['office:value']
-                       if 'table:formula' in attrs:
-                               datatype = 'formula'
-                               value = attrs['table:formula']
-                       if datatype == 'string':
-                               datatype = ""
-                               self.parser_cell_string_pending = True
-                               self.parser_cell_string_line = ""
-                       if 'table:number-columns-repeated' in attrs:
-                               self.parser_cell_repeats = int(attrs['table:number-columns-repeated']) - 1
-                       # Set the cell value
-                       if datatype:
-                               # I should do this once per cell repeat above 0
-                               for i in range(0, self.parser_cell_repeats+1):
-                                       self.set_cell_value(self.parser_sheet_column+i, self.parser_sheet_row, datatype, value)
-
-               # There are lots of interesting cases with table:table-cell data.  One problem is
-               # reading the number of embedded spaces correctly.  This code should help us get
-               # the number of spaces out.
-
-               if (self.parser_element == 'text:s'):
-                       # This means we have a number of spaces
-                       count_num = 0
-                       if 'text:c' in attrs:
-                               count_alpha = attrs['text:c']
-                               if (count_alpha.isdigit()):
-                                       count_num = int(count_alpha)
-                       # I am not sure what to do if we do not have a string pending
-                       if (self.parser_cell_string_pending == True):
-                               # Append the currect number of spaces to the end
-                               self.parser_cell_string_line = "%s%s" % (self.parser_cell_string_line, ' '*count_num)
-
-               if (self.parser_element == 'text:tab-stop'):
-                       if (self.parser_cell_string_pending == True):
-                               self.parser_cell_string_line = "%s\t" % (self.parser_cell_string_line)
-
-               if (self.parser_element == 'text:line-break'):
-                       if (self.parser_cell_string_pending == True):
-                               self.parser_cell_string_line = "%s\n" % (self.parser_cell_string_line)
-
-               # Debugging statements
-               if self.debug: print "  List: ", self.parser_element_list
-               if self.debug: print "  Attributes: ", attrs
-
-
-       def parse_content_end_element(self, name):
-               if self.debug: print '* End element:', name
-               if name != self.parser_element:
-                       print "Tag Mismatch: '%s' != '%s'" % (name, self.parser_element)
-               self.parser_element_list.pop()
-
-               # If the element was text:p and we are in string mode
-               if (self.parser_element == 'text:p'):
-                       if (self.parser_cell_string_pending):
-                               self.parser_cell_string_pending = False
-
-               # Take care of repeated cells
-               if (self.parser_element == 'table:table-cell'):
-                       self.parser_sheet_column += self.parser_cell_repeats
-
-               # Readjust parser_element_list and parser_element
-               if (self.parser_element_list):
-                       self.parser_element = self.parser_element_list[-1]
-               else:
-                       self.parser_element = ""
-
-       def parse_content_char_data(self, data):
-               if self.debug: print "  Character data: ", repr(data)
-
-               if (self.parser_element == 'text:p' or self.parser_element == 'text:span'):
-                       if (self.parser_cell_string_pending):
-                               # Set the string and leave string pending mode
-                               # This does feel a little kludgy, but it does the job
-                               self.parser_cell_string_line = "%s%s" % (self.parser_cell_string_line, data)
-
-                               # I should do this once per cell repeat above 0
-                               for i in range(0, self.parser_cell_repeats+1):
-                                       self.set_cell_value(self.parser_sheet_column+i, self.parser_sheet_row,
-                                                   'string', self.parser_cell_string_line)
-
-
-       def content_parse(self, data):
-               "Parse Content Data from a content.xml file"
-
-               # Debugging statements
-               if self.debug:
-                       # Sometimes it helps to see the document that was read from
-                       print data
-                       print "\n\n\n"
-
-               # Create parser
-               parser = xml.parsers.expat.ParserCreate()
-               # Set up parser callback functions
-               parser.StartElementHandler = self.parse_content_start_element
-               parser.EndElementHandler = self.parse_content_end_element
-               parser.CharacterDataHandler = self.parse_content_char_data
-
-               # Actually parse the data
-               parser.Parse(data, 1)
-
-       def save(self, filename):
-               """Save .ods spreadsheet.
-
-               The save function saves the current cells and settings into a document.
-               """
-               if self.debug: print "Writing %s" % filename
-               self.savefile = zipfile.ZipFile(filename, "w")
-               if self.debug: print "  meta.xml"
-               self._zip_insert(self.savefile, "meta.xml", self.meta.get_meta())
-               if self.debug: print "  mimetype"
-               self._zip_insert(self.savefile, "mimetype", "application/vnd.oasis.opendocument.spreadsheet")
-               if self.debug: print "  Configurations2/accelerator/current.xml"
-               self._zip_insert(self.savefile, "Configurations2/accelerator/current.xml", "")
-               if self.debug: print "  META-INF/manifest.xml"
-               self._zip_insert(self.savefile, "META-INF/manifest.xml", self._ods_manifest())
-               if self.debug: print "  content.xml"
-               self._zip_insert(self.savefile, "content.xml", self._ods_content())
-               if self.debug: print "  settings.xml"
-               self._zip_insert(self.savefile, "settings.xml", self._ods_settings())
-               if self.debug: print "  styles.xml"
-               self._zip_insert(self.savefile, "styles.xml", self._ods_styles())
-
-               # Add additional files if needed
-               for fileset in self.manifest_files:
-                       (filename, filetype, newname) = fileset
-                       # Read in the file
-                       data = self._file_load(filename)
-                       if self.debug: print "  Inserting '%s' as '%s'" % (filename, newname)
-                       self._zip_insert_binary(self.savefile, newname, data)
-
-       def _file_load(self, filename):
-               "Load a file"
-               file = open(filename, "rb")
-               data = file.read()
-               file.close()
-               return data
-
-       def _zip_insert_binary(self, file, filename, data):
-               "Insert a binary file into the zip archive"
-               now = time.localtime(time.time())[:6]
-               info = zipfile.ZipInfo(filename)
-               info.date_time = now
-               info.compress_type = zipfile.ZIP_DEFLATED
-               file.writestr(info, data)
-
-
-       def _zip_insert(self, file, filename, data):
-               "Insert a file into the zip archive"
-
-               # zip seems to struggle with non-ascii characters
-               data = data.encode('utf-8')
-
-               now = time.localtime(time.time())[:6]
-               info = zipfile.ZipInfo(filename)
-               info.date_time = now
-               info.compress_type = zipfile.ZIP_DEFLATED
-               file.writestr(info, data)
-
-       def _zip_read(self, file, filename):
-               "Get the data from a file in the zip archive by filename"
-               file = zipfile.ZipFile(file, "r")
-               data = file.read(filename)
-               # Need to close the file
-               file.close()
-               return data
-
-       def _ods_content(self):
-               "Generate ods content.xml data"
-
-               # This will list all of the sheets in the document
-               self.sheetdata = ['tag', 'office:spreadsheet']
-               for sheet in self.sheets:
-                       if self.debug:  
-                               sheet_name = sheet.get_name()
-                               print "    Creating Sheet '%s'" % sheet_name
-                       sheet_list = sheet.get_lists()
-                       self.sheetdata.append(sheet_list)
-               # Automatic Styles
-               self.automatic_styles = self.styles.get_automatic_styles()
-
-               self.data = ['tag', 'office:document-content',
-                 ['element', 'xmlns:office', 'urn:oasis:names:tc:opendocument:xmlns:office:1.0'],
-                 ['element', 'xmlns:style', 'urn:oasis:names:tc:opendocument:xmlns:style:1.0'],
-                 ['element', 'xmlns:text', 'urn:oasis:names:tc:opendocument:xmlns:text:1.0'],
-                 ['element', 'xmlns:table', 'urn:oasis:names:tc:opendocument:xmlns:table:1.0'],
-                 ['element', 'xmlns:draw', 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0'],
-                 ['element', 'xmlns:fo', 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0'],
-                 ['element', 'xmlns:xlink', 'http://www.w3.org/1999/xlink'],
-                 ['element', 'xmlns:dc', 'http://purl.org/dc/elements/1.1/'],
-                 ['element', 'xmlns:meta', 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0'],
-                 ['element', 'xmlns:number', 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0'],
-                 ['element', 'xmlns:svg', 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0'],
-                 ['element', 'xmlns:chart', 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0'],
-                 ['element', 'xmlns:dr3d', 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0'],
-                 ['element', 'xmlns:math', 'http://www.w3.org/1998/Math/MathML'],
-                 ['element', 'xmlns:form', 'urn:oasis:names:tc:opendocument:xmlns:form:1.0'],
-                 ['element', 'xmlns:script', 'urn:oasis:names:tc:opendocument:xmlns:script:1.0'],
-                 ['element', 'xmlns:ooo', 'http://openoffice.org/2004/office'],
-                 ['element', 'xmlns:ooow', 'http://openoffice.org/2004/writer'],
-                 ['element', 'xmlns:oooc', 'http://openoffice.org/2004/calc'],
-                 ['element', 'xmlns:dom', 'http://www.w3.org/2001/xml-events'],
-                 ['element', 'xmlns:xforms', 'http://www.w3.org/2002/xforms'],
-                 ['element', 'xmlns:xsd', 'http://www.w3.org/2001/XMLSchema'],
-                 ['element', 'xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance'],
-                 ['element', 'office:version', '1.0'],
-                 ['tagline', 'office:scripts'],
-                 ['tag', 'office:font-face-decls',
-                   ['tagline', 'style:font-face',
-                     ['element', 'style:name', 'DejaVu Sans'],
-                     ['element', 'svg:font-family', '&apos;DejaVu Sans&apos;'],
-                     ['element', 'style:font-pitch', 'variable']],
-                   ['tagline', 'style:font-face',
-                     ['element', 'style:name', 'Nimbus Sans L'],
-                     ['element', 'svg:font-family', '&apos;Nimbus Sans L&apos;'],
-                     ['element', 'style:font-family-generic', 'swiss'],
-                     ['element', 'style:font-pitch', 'variable']]],
-
-               # Automatic Styles
-               self.automatic_styles,
-
-                 ['tag', 'office:body',
-                   self.sheetdata]]                                      # Sheets are generated from the CalcSheet class
-
-               # Generate content.xml XML data
-               xml = XML()
-               self.lines = xml.convert(self.data)
-               self.filedata = '\n'.join(self.lines)
-               # Return generated data
-               return self.filedata
-
-       def _ods_manifest(self):
-               "Generate ods manifest.xml data"
-               self.data = ['tag', 'manifest:manifest',
-                 ['element', 'xmlns:manifest', 'urn:oasis:names:tc:opendocument:xmlns:manifest:1.0'],
-                 ['tagline', 'manifest:file-entry',
-                   ['element', 'manifest:media-type', 'application/vnd.oasis.opendocument.spreadsheet'],
-                   ['element', 'manifest:full-path', '/']],
-                 ['tagline', 'manifest:file-entry',
-                   ['element', 'manifest:media-type', 'application/vnd.sun.xml.ui.configuration'],
-                   ['element', 'manifest:full-path', 'Configurations2/']],
-                 ['tagline', 'manifest:file-entry',
-                   ['element', 'manifest:media-type', ''],
-                   ['element', 'manifest:full-path', 'Configurations2/accelerator/']],
-                 ['tagline', 'manifest:file-entry',
-                   ['element', 'manifest:media-type', ''],
-                   ['element', 'manifest:full-path', 'Configurations2/accelerator/current.xml']],
-                 ['tagline', 'manifest:file-entry',
-                   ['element', 'manifest:media-type', 'text/xml'],
-                   ['element', 'manifest:full-path', 'content.xml']],
-                 ['tagline', 'manifest:file-entry',
-                   ['element', 'manifest:media-type', 'text/xml'],
-                   ['element', 'manifest:full-path', 'styles.xml']],
-                 ['tagline', 'manifest:file-entry',
-                   ['element', 'manifest:media-type', 'text/xml'],
-                   ['element', 'manifest:full-path', 'meta.xml']],
-                 ['tagline', 'manifest:file-entry',
-                   ['element', 'manifest:media-type', 'text/xml'],
-                   ['element', 'manifest:full-path', 'settings.xml']]]
-
-               # Add additional files to manifest list
-               for fileset in self.manifest_files:
-                       (filename, filetype, newname) = fileset
-                       addfile = ['tagline', 'manifest:file-entry',
-                                  ['element', 'manifest:media-type', filetype],
-                                  ['element', 'manifest:full-path', newname]]
-                       self.data.append(addfile)
-
-               # Generate content.xml XML data
-               xml = XML()
-               self.lines = xml.convert(self.data)
-               self.filedata = '\n'.join(self.lines)
-               # Return generated data
-               return self.filedata
-
-
-       def _ods_settings(self):
-               "Generate ods settings.xml data"
-               self.data = ['tag', 'office:document-settings',
-                 ['element', 'xmlns:office', 'urn:oasis:names:tc:opendocument:xmlns:office:1.0'],
-                 ['element', 'xmlns:xlink', 'http://www.w3.org/1999/xlink'],
-                 ['element', 'xmlns:config', 'urn:oasis:names:tc:opendocument:xmlns:config:1.0'],
-                 ['element', 'xmlns:ooo', 'http://openoffice.org/2004/office'],
-                 ['element', 'office:version', '1.0'],
-                 ['tag', 'office:settings',
-                   ['tag', 'config:config-item-set',
-                     ['element', 'config:name', 'ooo:view-settings'],
-                     ['tag', 'config:config-item',
-                       ['element', 'config:name', 'VisibleAreaTop'],
-                       ['element', 'config:type', 'int'],
-                       ['data', '0']],
-                     ['tag', 'config:config-item',
-                       ['element', 'config:name', 'VisibleAreaLeft'],
-                       ['element', 'config:type', 'int'],
-                       ['data', '0']],
-                     ['tag', 'config:config-item',
-                       ['element', 'config:name', 'VisibleAreaWidth'],
-                       ['element', 'config:type', 'int'],
-                       ['data', '6774']],
-                     ['tag', 'config:config-item',
-                       ['element', 'config:name', 'VisibleAreaHeight'],
-                       ['element', 'config:type', 'int'],
-                       ['data', '2389']],
-                     ['tag', 'config:config-item-map-indexed',
-                       ['element', 'config:name', 'Views'],
-                       ['tag', 'config:config-item-map-entry',
-                         ['tag', 'config:config-item',
-                           ['element', 'config:name', 'ViewId'],
-                           ['element', 'config:type', 'string'],
-                           ['data', 'View1']],
-                         ['tag', 'config:config-item-map-named',
-                           ['element', 'config:name', 'Tables'],
-                           ['tag', 'config:config-item-map-entry',
-                             ['element', 'config:name', 'Sheet1'],
-                             ['tag', 'config:config-item',
-                               ['element', 'config:name', 'CursorPositionX'], # Cursor Position A
-                               ['element', 'config:type', 'int'],
-                               ['data', '0']],
-                             ['tag', 'config:config-item',
-                               ['element', 'config:name', 'CursorPositionY'], # Cursor Position 1
-                               ['element', 'config:type', 'int'],
-                               ['data', '0']],
-                             ['tag', 'config:config-item',
-                               ['element', 'config:name', 'HorizontalSplitMode'],
-                               ['element', 'config:type', 'short'],
-                               ['data', '0']],
-                             ['tag', 'config:config-item',
-                               ['element', 'config:name', 'VerticalSplitMode'],
-                               ['element', 'config:type', 'short'],
-                               ['data', '0']],
-                             ['tag', 'config:config-item',
-                               ['element', 'config:name', 'HorizontalSplitPosition'],
-                               ['element', 'config:type', 'int'],
-                               ['data', '0']],
-                             ['tag', 'config:config-item',
-                               ['element', 'config:name', 'VerticalSplitPosition'],
-                               ['element', 'config:type', 'int'],
-                               ['data', '0']],
-                             ['tag', 'config:config-item',
-                               ['element', 'config:name', 'ActiveSplitRange'],
-                               ['element', 'config:type', 'short'],
-                               ['data', '2']],
-                             ['tag', 'config:config-item',
-                               ['element', 'config:name', 'PositionLeft'],
-                               ['element', 'config:type', 'int'],
-                               ['data', '0']],
-                             ['tag', 'config:config-item',
-                               ['element', 'config:name', 'PositionRight'],
-                               ['element', 'config:type', 'int'],
-                               ['data', '0']],
-                             ['tag', 'config:config-item',
-                               ['element', 'config:name', 'PositionTop'],
-                               ['element', 'config:type', 'int'],
-                               ['data', '0']],
-                             ['tag', 'config:config-item',
-                               ['element', 'config:name', 'PositionBottom'],
-                               ['element', 'config:type', 'int'],
-                               ['data', '0']]]],
-                         ['tag', 'config:config-item',
-                           ['element', 'config:name', 'ActiveTable'],
-                           ['element', 'config:type', 'string'],
-                           ['data', 'Sheet1']],
-                         ['tag', 'config:config-item',
-                           ['element', 'config:name', 'HorizontalScrollbarWidth'],
-                           ['element', 'config:type', 'int'],
-                           ['data', '270']],
-                         ['tag', 'config:config-item',
-                           ['element', 'config:name', 'ZoomType'],
-                           ['element', 'config:type', 'short'],
-                           ['data', '0']],
-                         ['tag', 'config:config-item',
-                           ['element', 'config:name', 'ZoomValue'],
-                           ['element', 'config:type', 'int'],
-                           ['data', '100']],
-                         ['tag', 'config:config-item',
-                           ['element', 'config:name', 'PageViewZoomValue'],
-                           ['element', 'config:type', 'int'],
-                           ['data', '60']],
-                         ['tag', 'config:config-item',
-                           ['element', 'config:name', 'ShowPageBreakPreview'],
-                           ['element', 'config:type', 'boolean'],
-                           ['data', 'false']],
-                         ['tag', 'config:config-item',
-                           ['element', 'config:name', 'ShowZeroValues'],
-                           ['element', 'config:type', 'boolean'],
-                           ['data', 'true']],
-                         ['tag', 'config:config-item',
-                           ['element', 'config:name', 'ShowNotes'],
-                           ['element', 'config:type', 'boolean'],
-                           ['data', 'true']],
-                         ['tag', 'config:config-item',
-                           ['element', 'config:name', 'ShowGrid'],
-                           ['element', 'config:type', 'boolean'],
-                           ['data', 'true']],
-                         ['tag', 'config:config-item',
-                           ['element', 'config:name', 'GridColor'],
-                           ['element', 'config:type', 'long'],
-                           ['data', '12632256']],
-                         ['tag', 'config:config-item',
-                           ['element', 'config:name', 'ShowPageBreaks'],
-                           ['element', 'config:type', 'boolean'],
-                           ['data', 'true']],
-                         ['tag', 'config:config-item',
-                           ['element', 'config:name', 'HasColumnRowHeaders'],
-                           ['element', 'config:type', 'boolean'],
-                           ['data', 'true']],
-                         ['tag', 'config:config-item',
-                           ['element', 'config:name', 'HasSheetTabs'],
-                           ['element', 'config:type', 'boolean'],
-                           ['data', 'true']],
-                         ['tag', 'config:config-item',
-                           ['element', 'config:name', 'IsOutlineSymbolsSet'],
-                           ['element', 'config:type', 'boolean'],
-                           ['data', 'true']],
-                         ['tag', 'config:config-item',
-                           ['element', 'config:name', 'IsSnapToRaster'],
-                           ['element', 'config:type', 'boolean'],
-                           ['data', 'false']],
-                         ['tag', 'config:config-item',
-                           ['element', 'config:name', 'RasterIsVisible'],
-                           ['element', 'config:type', 'boolean'],
-                           ['data', 'false']],
-                         ['tag', 'config:config-item',
-                           ['element', 'config:name', 'RasterResolutionX'],
-                           ['element', 'config:type', 'int'],
-                           ['data', '1270']],
-                         ['tag', 'config:config-item',
-                           ['element', 'config:name', 'RasterResolutionY'],
-                           ['element', 'config:type', 'int'],
-                           ['data', '1270']],
-                         ['tag', 'config:config-item',
-                           ['element', 'config:name', 'RasterSubdivisionX'],
-                           ['element', 'config:type', 'int'],
-                           ['data', '1']],
-                         ['tag', 'config:config-item',
-                           ['element', 'config:name', 'RasterSubdivisionY'],
-                           ['element', 'config:type', 'int'],
-                           ['data', '1']],
-                         ['tag', 'config:config-item',
-                           ['element', 'config:name', 'IsRasterAxisSynchronized'],
-                           ['element', 'config:type', 'boolean'],
-                           ['data', 'true']]]]],
-                   ['tag', 'config:config-item-set',
-                     ['element', 'config:name', 'ooo:configuration-settings'],
-                     ['tag', 'config:config-item',
-                       ['element', 'config:name', 'ShowZeroValues'],
-                       ['element', 'config:type', 'boolean'],
-                       ['data', 'true']],
-                     ['tag', 'config:config-item',
-                       ['element', 'config:name', 'ShowNotes'],
-                       ['element', 'config:type', 'boolean'],
-                       ['data', 'true']],
-                     ['tag', 'config:config-item',
-                       ['element', 'config:name', 'ShowGrid'],
-                       ['element', 'config:type', 'boolean'],
-                       ['data', 'true']],
-                     ['tag', 'config:config-item',
-                       ['element', 'config:name', 'GridColor'],
-                       ['element', 'config:type', 'long'],
-                       ['data', '12632256']],
-                     ['tag', 'config:config-item',
-                       ['element', 'config:name', 'ShowPageBreaks'],
-                       ['element', 'config:type', 'boolean'],
-                       ['data', 'true']],
-                     ['tag', 'config:config-item',
-                       ['element', 'config:name', 'LinkUpdateMode'],
-                       ['element', 'config:type', 'short'],
-                       ['data', '3']],
-                     ['tag', 'config:config-item',
-                       ['element', 'config:name', 'HasColumnRowHeaders'],
-                       ['element', 'config:type', 'boolean'],
-                       ['data', 'true']],
-                     ['tag', 'config:config-item',
-                       ['element', 'config:name', 'HasSheetTabs'],
-                       ['element', 'config:type', 'boolean'],
-                       ['data', 'true']],
-                     ['tag', 'config:config-item',
-                       ['element', 'config:name', 'IsOutlineSymbolsSet'],
-                       ['element', 'config:type', 'boolean'],
-                       ['data', 'true']],
-                     ['tag', 'config:config-item',
-                       ['element', 'config:name', 'IsSnapToRaster'],
-                       ['element', 'config:type', 'boolean'],
-                       ['data', 'false']],
-                     ['tag', 'config:config-item',
-                       ['element', 'config:name', 'RasterIsVisible'],
-                       ['element', 'config:type', 'boolean'],
-                       ['data', 'false']],
-                     ['tag', 'config:config-item',
-                       ['element', 'config:name', 'RasterResolutionX'],
-                       ['element', 'config:type', 'int'],
-                       ['data', '1270']],
-                     ['tag', 'config:config-item',
-                       ['element', 'config:name', 'RasterResolutionY'],
-                       ['element', 'config:type', 'int'],
-                       ['data', '1270']],
-                     ['tag', 'config:config-item',
-                       ['element', 'config:name', 'RasterSubdivisionX'],
-                       ['element', 'config:type', 'int'],
-                       ['data', '1']],
-                     ['tag', 'config:config-item',
-                       ['element', 'config:name', 'RasterSubdivisionY'],
-                       ['element', 'config:type', 'int'],
-                       ['data', '1']],
-                     ['tag', 'config:config-item',
-                       ['element', 'config:name', 'IsRasterAxisSynchronized'],
-                       ['element', 'config:type', 'boolean'],
-                       ['data', 'true']],
-                     ['tag', 'config:config-item',
-                       ['element', 'config:name', 'AutoCalculate'],
-                       ['element', 'config:type', 'boolean'],
-                       ['data', 'true']],
-                     ['tag', 'config:config-item',
-                       ['element', 'config:name', 'PrinterName'],
-                       ['element', 'config:type', 'string'],
-                       ['data', 'Generic Printer']],
-                     ['tag', 'config:config-item',
-                       ['element', 'config:name', 'PrinterSetup'],
-                       ['element', 'config:type', 'base64Binary'],
-                       ['data', 'YgH+/0dlbmVyaWMgUHJpbnRlcgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU0dFTlBSVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWAAMAqAAAAAAA//8FAFZUAAAkbQAASm9iRGF0YSAxCnByaW50ZXI9R2VuZXJpYyBQcmludGVyCm9yaWVudGF0aW9uPVBvcnRyYWl0CmNvcGllcz0xCnNjYWxlPTEwMAptYXJnaW5kYWp1c3RtZW50PTAsMCwwLDAKY29sb3JkZXB0aD0yNApwc2xldmVsPTAKY29sb3JkZXZpY2U9MApQUERDb250ZXhEYXRhClBhZ2VTaXplOkxldHRlcgAA']],
-                     ['tag', 'config:config-item',
-                       ['element', 'config:name', 'ApplyUserData'],
-                       ['element', 'config:type', 'boolean'],
-                       ['data', 'true']],
-                     ['tag', 'config:config-item',
-                       ['element', 'config:name', 'CharacterCompressionType'],
-                       ['element', 'config:type', 'short'],
-                       ['data', '0']],
-                     ['tag', 'config:config-item',
-                       ['element', 'config:name', 'IsKernAsianPunctuation'],
-                       ['element', 'config:type', 'boolean'],
-                       ['data', 'false']],
-                     ['tag', 'config:config-item',
-                       ['element', 'config:name', 'SaveVersionOnClose'],
-                       ['element', 'config:type', 'boolean'],
-                       ['data', 'false']],
-                     ['tag', 'config:config-item',
-                       ['element', 'config:name', 'UpdateFromTemplate'],
-                       ['element', 'config:type', 'boolean'],
-                       ['data', 'false']],
-                     ['tag', 'config:config-item',
-                       ['element', 'config:name', 'AllowPrintJobCancel'],
-                       ['element', 'config:type', 'boolean'],
-                       ['data', 'true']],
-                     ['tag', 'config:config-item',
-                       ['element', 'config:name', 'LoadReadonly'],
-                       ['element', 'config:type', 'boolean'],
-                       ['data', 'false']]]]]
-
-               # Generate content.xml XML data
-               xml = XML()
-               self.lines = xml.convert(self.data)
-               self.filedata = '\n'.join(self.lines)
-               # Return generated data
-               return self.filedata
-
-
-       def _ods_styles(self):
-               "Generate ods styles.xml data"
-               self.data = ['tag', 'office:document-styles',
-                 ['element', 'xmlns:office', 'urn:oasis:names:tc:opendocument:xmlns:office:1.0'],
-                 ['element', 'xmlns:style', 'urn:oasis:names:tc:opendocument:xmlns:style:1.0'],
-                 ['element', 'xmlns:text', 'urn:oasis:names:tc:opendocument:xmlns:text:1.0'],
-                 ['element', 'xmlns:table', 'urn:oasis:names:tc:opendocument:xmlns:table:1.0'],
-                 ['element', 'xmlns:draw', 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0'],
-                 ['element', 'xmlns:fo', 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0'],
-                 ['element', 'xmlns:xlink', 'http://www.w3.org/1999/xlink'],
-                 ['element', 'xmlns:dc', 'http://purl.org/dc/elements/1.1/'],
-                 ['element', 'xmlns:meta', 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0'],
-                 ['element', 'xmlns:number', 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0'],
-                 ['element', 'xmlns:svg', 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0'],
-                 ['element', 'xmlns:chart', 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0'],
-                 ['element', 'xmlns:dr3d', 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0'],
-                 ['element', 'xmlns:math', 'http://www.w3.org/1998/Math/MathML'],
-                 ['element', 'xmlns:form', 'urn:oasis:names:tc:opendocument:xmlns:form:1.0'],
-                 ['element', 'xmlns:script', 'urn:oasis:names:tc:opendocument:xmlns:script:1.0'],
-                 ['element', 'xmlns:ooo', 'http://openoffice.org/2004/office'],
-                 ['element', 'xmlns:ooow', 'http://openoffice.org/2004/writer'],
-                 ['element', 'xmlns:oooc', 'http://openoffice.org/2004/calc'],
-                 ['element', 'xmlns:dom', 'http://www.w3.org/2001/xml-events'],
-                 ['element', 'office:version', '1.0'],
-                 ['tag', 'office:font-face-decls',
-                   ['tagline', 'style:font-face',
-                     ['element', 'style:name', 'DejaVu Sans'],
-                     ['element', 'svg:font-family', '&apos;DejaVu Sans&apos;'],
-                     ['element', 'style:font-pitch', 'variable']],
-                   ['tagline', 'style:font-face',
-                     ['element', 'style:name', 'Nimbus Sans L'],
-                     ['element', 'svg:font-family', '&apos;Nimbus Sans L&apos;'],
-                     ['element', 'style:font-family-generic', 'swiss'],
-                     ['element', 'style:font-pitch', 'variable']]],
-                 ['tag', 'office:styles',
-                   ['tag', 'style:default-style',
-                     ['element', 'style:family', 'table-cell'],
-                     ['tagline', 'style:table-cell-properties',
-                       ['element', 'style:decimal-places', '2']],
-                     ['tagline', 'style:paragraph-properties',
-                       ['element', 'style:tab-stop-distance', '0.5in']],
-                     ['tagline', 'style:text-properties',
-                       ['element', 'style:font-name', 'Nimbus Sans L'],
-                       ['element', 'fo:language', 'en'],
-                       ['element', 'fo:country', 'US'],
-                       ['element', 'style:font-name-asian', 'DejaVu Sans'],
-                       ['element', 'style:language-asian', 'none'],
-                       ['element', 'style:country-asian', 'none'],
-                       ['element', 'style:font-name-complex', 'DejaVu Sans'],
-                       ['element', 'style:language-complex', 'none'],
-                       ['element', 'style:country-complex', 'none']]],
-                   ['tag', 'number:number-style',
-                     ['element', 'style:name', 'N0'],
-                     ['tagline', 'number:number',
-                       ['element', 'number:min-integer-digits', '1']]],
-                   ['tag', 'number:currency-style',
-                     ['element', 'style:name', 'N104P0'],
-                     ['element', 'style:volatile', 'true'],
-                     ['tag', 'number:currency-symbol',
-                       ['element', 'number:language', 'en'],
-                       ['element', 'number:country', 'US'],
-                       ['data', '$']],
-                     ['tagline', 'number:number',
-                       ['element', 'number:decimal-places', '2'],
-                       ['element', 'number:min-integer-digits', '1'],
-                       ['element', 'number:grouping', 'true']]],
-                   ['tag', 'number:currency-style',
-                     ['element', 'style:name', 'N104'],
-                     ['tagline', 'style:text-properties',
-                       ['element', 'fo:color', '#ff0000']],
-                     ['tag', 'number:text',
-                       ['data', '-']],
-                     ['tag', 'number:currency-symbol',
-                       ['element', 'number:language', 'en'],
-                       ['element', 'number:country', 'US'],
-                       ['data', '$']],
-                     ['tagline', 'number:number', 
-                       ['element', 'number:decimal-places', '2'],
-                       ['element', 'number:min-integer-digits', '1'],
-                       ['element', 'number:grouping', 'true']],
-                     ['tagline', 'style:map',
-                       ['element', 'style:condition', 'value()&gt;=0'],
-                       ['element', 'style:apply-style-name', 'N104P0']]],
-                   ['tagline', 'style:style',
-                     ['element', 'style:name', 'Default'],
-                     ['element', 'style:family', 'table-cell']],
-                   ['tag', 'style:style',
-                     ['element', 'style:name', 'Result'],
-                     ['element', 'style:family', 'table-cell'],
-                     ['element', 'style:parent-style-name', 'Default'],
-                     ['tagline', 'style:text-properties',
-                       ['element', 'fo:font-style', 'italic'],
-                       ['element', 'style:text-underline-style', 'solid'],
-                       ['element', 'style:text-underline-width', 'auto'],
-                       ['element', 'style:text-underline-color', 'font-color'],
-                       ['element', 'fo:font-weight', 'bold']]],
-                   ['tagline', 'style:style',
-                     ['element', 'style:name', 'Result2'],
-                     ['element', 'style:family', 'table-cell'],
-                     ['element', 'style:parent-style-name', 'Result'],
-                     ['element', 'style:data-style-name', 'N104']],
-                   ['tag', 'style:style',
-                     ['element', 'style:name', 'Heading'],
-                     ['element', 'style:family', 'table-cell'],
-                     ['element', 'style:parent-style-name', 'Default'],
-                     ['tagline', 'style:table-cell-properties',
-                       ['element', 'style:text-align-source', 'fix'],
-                       ['element', 'style:repeat-content', 'false']],
-                     ['tagline', 'style:paragraph-properties',
-                       ['element', 'fo:text-align', 'center']],
-                     ['tagline', 'style:text-properties',
-                       ['element', 'fo:font-size', '16pt'],
-                       ['element', 'fo:font-style', 'italic'],
-                       ['element', 'fo:font-weight', 'bold']]],
-                   ['tag', 'style:style',
-                     ['element', 'style:name', 'Heading1'],
-                     ['element', 'style:family', 'table-cell'],
-                     ['element', 'style:parent-style-name', 'Heading'],
-                     ['tagline', 'style:table-cell-properties',
-                       ['element', 'style:rotation-angle', '90']]]],
-                 ['tag', 'office:automatic-styles',
-                   ['tag', 'style:page-layout',
-                     ['element', 'style:name', 'pm1'],
-                     ['tagline', 'style:page-layout-properties',
-                       ['element', 'style:writing-mode', 'lr-tb']],
-                     ['tag', 'style:header-style',
-                       ['tagline', 'style:header-footer-properties',
-                         ['element', 'fo:min-height', '0.2957in'],
-                         ['element', 'fo:margin-left', '0in'],
-                         ['element', 'fo:margin-right', '0in'],
-                         ['element', 'fo:margin-bottom', '0.0984in']]],
-                     ['tag', 'style:footer-style',
-                       ['tagline', 'style:header-footer-properties',
-                         ['element', 'fo:min-height', '0.2957in'],
-                         ['element', 'fo:margin-left', '0in'],
-                         ['element', 'fo:margin-right', '0in'],
-                         ['element', 'fo:margin-top', '0.0984in']]]],
-                   ['tag', 'style:page-layout',
-                     ['element', 'style:name', 'pm2'],
-                     ['tagline', 'style:page-layout-properties',
-                       ['element', 'style:writing-mode', 'lr-tb']],
-                     ['tag', 'style:header-style',
-                       ['tag', 'style:header-footer-properties',
-                         ['element', 'fo:min-height', '0.2957in'],
-                         ['element', 'fo:margin-left', '0in'],
-                         ['element', 'fo:margin-right', '0in'],
-                         ['element', 'fo:margin-bottom', '0.0984in'],
-                         ['element', 'fo:border', '0.0346in solid #000000'],
-                         ['element', 'fo:padding', '0.0071in'],
-                         ['element', 'fo:background-color', '#c0c0c0'],
-                         ['tagline', 'style:background-image']]],
-                     ['tag', 'style:footer-style',
-                       ['tag', 'style:header-footer-properties',
-                         ['element', 'fo:min-height', '0.2957in'],
-                         ['element', 'fo:margin-left', '0in'],
-                         ['element', 'fo:margin-right', '0in'],
-                         ['element', 'fo:margin-top', '0.0984in'],
-                         ['element', 'fo:border', '0.0346in solid #000000'],
-                         ['element', 'fo:padding', '0.0071in'],
-                         ['element', 'fo:background-color', '#c0c0c0'],
-                         ['tagline', 'style:background-image']]]]],
-                 ['tag', 'office:master-styles',
-                   ['tag', 'style:master-page',
-                     ['element', 'style:name', 'Default'],
-                     ['element', 'style:page-layout-name', 'pm1'],
-                     ['tag', 'style:header',
-                       ['tag', 'text:p',
-                         ['data', '<text:sheet-name>???</text:sheet-name>']]],
-                     ['tagline', 'style:header-left',
-                       ['element', 'style:display', 'false']],
-                     ['tag', 'style:footer',
-                       ['tag', 'text:p',
-                         ['data', 'Page <text:page-number>1</text:page-number>']]],
-                     ['tagline', 'style:footer-left',
-                       ['element', 'style:display', 'false']]],
-                   ['tag', 'style:master-page',
-                     ['element', 'style:name', 'Report'],
-                     ['element', 'style:page-layout-name', 'pm2'],
-                     ['tag', 'style:header',
-                       ['tag', 'style:region-left',
-                         ['tag', 'text:p',
-                           ['data', '<text:sheet-name>???</text:sheet-name> (<text:title>???</text:title>)']]],
-                       ['tag', 'style:region-right',
-                         ['tag', 'text:p',
-                           ['data', '<text:date style:data-style-name="N2" text:date-value="2006-09-29">09/29/2006</text:date>, <text:time>13:02:56</text:time>']]]],
-                     ['tagline', 'style:header-left',
-                       ['element', 'style:display', 'false']],
-                     ['tag', 'style:footer',
-                       ['tag', 'text:p',
-                         ['data', 'Page <text:page-number>1</text:page-number> / <text:page-count>99</text:page-count>']]],
-                     ['tagline', 'style:footer-left',
-                       ['element', 'style:display', 'false']]]]]
-
-
-               # Generate content.xml XML data
-               xml = XML()
-               self.lines = xml.convert(self.data)
-               self.filedata = '\n'.join(self.lines)
-               # Return generated data
-               return self.filedata
+    "Calc Class - Used to create OpenDocument Format Calc Spreadsheets."
+
+    def __init__(self, sheetname=None, opendoc=None, debug=False):
+        "Initialize ooolib Calc instance"
+        # Default to no debugging
+        self.debug = debug
+        if not sheetname: sheetname = "Sheet1"
+        self.sheets = [CalcSheet(sheetname)]  # The main sheet will be initially called 'Sheet1'
+        self.sheet_index = 0                  # We initially start on the first sheet
+        self.styles = CalcStyles()
+        self.meta = Meta('ods')
+        self.styles.get_style_code('column')  # Force generation of default column
+        self.styles.get_style_code('row')     # Force generation of default row
+        self.styles.get_style_code('table')   # Force generation of default table
+        self.styles.get_style_code('cell')    # Force generation of default cell
+        self.manifest_files = []              # List of extra files included
+        self.manifest_index = 1               # Index of added manifest files
+        # Data Parsing
+        self.parser_element_list = []
+        self.parser_element = ""
+        self.parser_sheet_num = 0
+        self.parser_sheet_row = 0
+        self.parser_sheet_column = 0
+        self.parser_cell_repeats = 0
+        self.parser_cell_string_pending = False
+        self.parser_cell_string_line = ""
+        # See if we need to read a document
+        if opendoc:
+            # Verify that the document exists
+            if self.debug: print("Opening Document: %s" % opendoc)
+            # Okay, now we load the file
+            self.load(opendoc)
+
+    def debug_level(self, level):
+        """Set debug level:
+        True if you want debugging messages
+        False if you do not.
+        """
+        self.debug = level
+
+    def file_mimetype(self, filename):
+        "Determine the filetype from the filename"
+        parts = filename.lower().split('.')
+        ext = parts[-1]
+        if (ext == 'png'): return (ext, "image/png")
+        if (ext == 'gif'): return (ext, "image/gif")
+        return (ext, "image/unknown")
+
+    def add_file(self, filename):
+        """Prepare a file for loading into ooolib
+
+        The filename should be the local filesystem name for
+        the file.  The file is then prepared to be included in
+        the creation of the final document.  The file needs to
+        remain in place so that it is available when the actual
+        document creation happens.
+        """
+        # mimetype set to (ext, filetype)
+        mimetype = self.file_mimetype(filename)
+        newname = "Pictures/%08d.%s" % (self.manifest_index, mimetype[0])
+        self.manifest_index += 1
+        filetype = mimetype[1]
+        self.manifest_files.append((filename, filetype, newname))
+        return newname
+
+    def set_meta(self, metaname, value):
+        "Set meta data in your document."
+        self.meta.set_meta(metaname, value)
+
+    def get_meta_value(self, metaname):
+        "Get meta data value for a given metaname"
+        return self.meta.get_meta_value(metaname)
+
+    def get_sheet_name(self):
+        "Returns the sheet name"
+        return self.sheets[self.sheet_index].get_name()
+
+    def get_sheet_dimensions(self):
+        "Returns the sheet dimensions in (cols, rows)"
+        return self.sheets[self.sheet_index].get_sheet_dimensions()
+
+    def set_column_property(self, column, name, value):
+        "Set Column Properties"
+        if name == 'width':
+            # column number column needs column-width set to value
+            self.styles.set_property('column', 'style:column-width', value)
+            style_code = self.styles.get_style_code('column')
+            self.sheets[self.sheet_index].set_sheet_config(('col', column), style_code)
+
+    def set_row_property(self, row, name, value):
+        "Set row Properties"
+        if name == 'height':
+            # row number row needs row-height set to value
+            self.styles.set_property('row', 'style:row-height', value)
+            style_code = self.styles.get_style_code('row')
+            self.sheets[self.sheet_index].set_sheet_config(('row', row), style_code)
+
+    def set_cell_property(self, name, value):
+        """Turn and off cell properties
+
+        Actual application of properties is handled by setting a value."""
+        # background images need to be handled a little differently
+        # because they need to also be inserted into the final document
+        if (name == 'backgroundimage'):
+            # Add file and modify value
+            value = self.add_file(value)
+        self.styles.set_property('cell', name, value)
+
+    def get_sheet_index(self):
+        "Return the current sheet index number"
+        return self.sheet_index
+
+    def set_sheet_index(self, index):
+        "Set the sheet index"
+        if type(index) == type(1):
+            if index >= 0 and index < len(self.sheets):
+                self.sheet_index = index
+        return self.sheet_index
+
+    def get_sheet_count(self):
+        "Returns the number of existing sheets"
+        return len(self.sheets)
+
+    def new_sheet(self, sheetname):
+        "Create a new sheet"
+        self.sheet_index = len(self.sheets)
+        self.sheets.append(CalcSheet(sheetname))
+        return self.sheet_index
+
+    def set_cell_value(self, col, row, datatype, value):
+        "Set the value for a given cell"
+        self.sheets[self.sheet_index].set_sheet_value((col, row), datatype, value)
+        style_code = self.styles.get_style_code('cell')
+        self.sheets[self.sheet_index].set_sheet_config((col, row), style_code)
+
+    def get_cell_value(self, col, row):
+        "Get a cell value tuple (type, value) for a given cell"
+        sheetvalue = self.sheets[self.sheet_index].get_sheet_value(col, row)
+        # We stop here if there is no value for sheetvalue
+        if sheetvalue == None: return sheetvalue
+        # Now check to see if we have a value tuple
+        if 'value' in sheetvalue:
+            return sheetvalue['value']
+        else:
+            return None
+
+    def load(self, filename):
+        """Load .ods spreadsheet.
+
+        The load function loads data from a document into the current cells.
+        """
+        # Read in the important files
+        # meta.xml
+        data = self._zip_read(filename, "meta.xml")
+        self.meta.meta_parse(data)        
+        # content.xml
+        data = self._zip_read(filename, "content.xml")
+        self.content_parse(data)
+        # settings.xml - I do not remember putting anything here
+        # styles.xml - I do not remember putting anything here
+
+    def parse_content_start_element(self, name, attrs):
+        if self.debug: print('* Start element:', name)
+        self.parser_element_list.append(name)
+        self.parser_element = self.parser_element_list[-1]
+        # Keep track of the current sheet number
+        if (self.parser_element == 'table:table'):
+            # Move to starting cell
+            self.parser_sheet_row = 0
+            self.parser_sheet_column = 0
+            # Increment the sheet number count
+            self.parser_sheet_num += 1
+            if (self.parser_sheet_num - 1 != self.sheet_index):
+                # We are not on the first sheet and need to create a new sheet.
+                # We will automatically move to the new sheet
+                sheetname = "Sheet%d" % self.parser_sheet_num
+                if 'table:name' in attrs: sheetname = attrs['table:name']
+                self.new_sheet(sheetname)
+            else:
+                # We are on the first sheet and will need to overwrite the default name
+                sheetname = "Sheet%d" % self.parser_sheet_num
+                if 'table:name' in attrs: sheetname = attrs['table:name']
+                self.sheets[self.sheet_index].set_name(sheetname)
+        # Update the row numbers
+        if (self.parser_element == 'table:table-row'):
+            self.parser_sheet_row += 1
+            self.parser_sheet_column = 0
+        # Okay, now keep track of the sheet cell data
+        if (self.parser_element == 'table:table-cell'):
+            # By default it will repeat zero times
+            self.parser_cell_repeats = 0
+            # We must be in a new column
+            self.parser_sheet_column += 1
+            # Set some default values
+            datatype = ""
+            value = ""
+            # Get values from attrs hash
+            if 'office:value-type' in attrs: datatype = attrs['office:value-type']
+            if 'office:value' in attrs: value = attrs['office:value']
+            if 'table:formula' in attrs:
+                datatype = 'formula'
+                value = attrs['table:formula']
+            if datatype == 'string':
+                datatype = ""
+                self.parser_cell_string_pending = True
+                self.parser_cell_string_line = ""
+            if 'table:number-columns-repeated' in attrs:
+                self.parser_cell_repeats = int(attrs['table:number-columns-repeated']) - 1
+            # Set the cell value
+            if datatype:
+                # I should do this once per cell repeat above 0
+                for i in range(0, self.parser_cell_repeats+1):
+                    self.set_cell_value(self.parser_sheet_column+i, self.parser_sheet_row, datatype, value)
+        # There are lots of interesting cases with table:table-cell data.  One problem is
+        # reading the number of embedded spaces correctly.  This code should help us get
+        # the number of spaces out.
+        if (self.parser_element == 'text:s'):
+            # This means we have a number of spaces
+            count_num = 0
+            if 'text:c' in attrs:
+                count_alpha = attrs['text:c']
+                if (count_alpha.isdigit()):
+                    count_num = int(count_alpha)
+            # I am not sure what to do if we do not have a string pending
+            if (self.parser_cell_string_pending == True):
+                # Append the currect number of spaces to the end
+                self.parser_cell_string_line = "%s%s" % (self.parser_cell_string_line, ' '*count_num)
+        if (self.parser_element == 'text:tab-stop'):
+            if (self.parser_cell_string_pending == True):
+                self.parser_cell_string_line = "%s\t" % (self.parser_cell_string_line)
+        if (self.parser_element == 'text:line-break'):
+            if (self.parser_cell_string_pending == True):
+                self.parser_cell_string_line = "%s\n" % (self.parser_cell_string_line)
+        # Debugging statements
+        if self.debug: print("  List: ", self.parser_element_list)
+        if self.debug: print("  Attributes: ", attrs)
+
+    def parse_content_end_element(self, name):
+        if self.debug: print('* End element:', name)
+        if name != self.parser_element:
+            print("Tag Mismatch: '%s' != '%s'" % (name, self.parser_element))
+        self.parser_element_list.pop()
+        # If the element was text:p and we are in string mode
+        if (self.parser_element == 'text:p'):
+            if (self.parser_cell_string_pending):
+                self.parser_cell_string_pending = False
+        # Take care of repeated cells
+        if (self.parser_element == 'table:table-cell'):
+            self.parser_sheet_column += self.parser_cell_repeats
+        # Readjust parser_element_list and parser_element
+        if (self.parser_element_list):
+            self.parser_element = self.parser_element_list[-1]
+        else:
+            self.parser_element = ""
+
+    def parse_content_char_data(self, data):
+        if self.debug:
+            print("  Character data: ", repr(data))
+        if (self.parser_element == 'text:p' or self.parser_element == 'text:span'):
+            if (self.parser_cell_string_pending):
+                # Set the string and leave string pending mode
+                # This does feel a little kludgy, but it does the job
+                self.parser_cell_string_line = "%s%s" % (self.parser_cell_string_line, data)
+                # I should do this once per cell repeat above 0
+                for i in range(0, self.parser_cell_repeats+1):
+                    self.set_cell_value(self.parser_sheet_column+i, self.parser_sheet_row,
+                            'string', self.parser_cell_string_line)
+
+    def content_parse(self, data):
+        "Parse Content Data from a content.xml file"
+        # Debugging statements
+        if self.debug:
+            # Sometimes it helps to see the document that was read from
+            print(data)
+            print("\n\n\n")
+        # Create parser
+        parser = xml.parsers.expat.ParserCreate()
+        # Set up parser callback functions
+        parser.StartElementHandler = self.parse_content_start_element
+        parser.EndElementHandler = self.parse_content_end_element
+        parser.CharacterDataHandler = self.parse_content_char_data
+        # Actually parse the data
+        parser.Parse(data, 1)
+
+    def save(self, filename):
+        """Save .ods spreadsheet.
+
+        The save function saves the current cells and settings into a document.
+        """
+        if self.debug: print("Writing %s" % filename)
+        self.savefile = zipfile.ZipFile(filename, "w")
+        if self.debug: print("  meta.xml")
+        self._zip_insert(self.savefile, "meta.xml", self.meta.get_meta())
+        if self.debug: print("  mimetype")
+        self._zip_insert(self.savefile, "mimetype", "application/vnd.oasis.opendocument.spreadsheet")
+        if self.debug: print("  Configurations2/accelerator/current.xml")
+        self._zip_insert(self.savefile, "Configurations2/accelerator/current.xml", "")
+        if self.debug: print("  META-INF/manifest.xml")
+        self._zip_insert(self.savefile, "META-INF/manifest.xml", self._ods_manifest())
+        if self.debug: print("  content.xml")
+        self._zip_insert(self.savefile, "content.xml", self._ods_content())
+        if self.debug: print("  settings.xml")
+        self._zip_insert(self.savefile, "settings.xml", self._ods_settings())
+        if self.debug: print("  styles.xml")
+        self._zip_insert(self.savefile, "styles.xml", self._ods_styles())
+        # Add additional files if needed
+        for fileset in self.manifest_files:
+            (filename, filetype, newname) = fileset
+            # Read in the file
+            data = self._file_load(filename)
+            if self.debug: print("  Inserting '%s' as '%s'" % (filename, newname))
+            self._zip_insert_binary(self.savefile, newname, data)
+
+    def _file_load(self, filename):
+        "Load a file"
+        file = open(filename, "r")
+        data = file.read()
+        file.close()
+        return data
+
+    def _zip_insert_binary(self, file, filename, data):
+        "Insert a binary file into the zip archive"
+        now = time.localtime(time.time())[:6]
+        info = zipfile.ZipInfo(filename)
+        info.date_time = now
+        info.compress_type = zipfile.ZIP_DEFLATED
+        file.writestr(info, data)
+
+    def _zip_insert(self, file, filename, data):
+        "Insert a file into the zip archive"
+        # zip seems to struggle with non-ascii characters
+        data = data.encode('utf-8')
+        now = time.localtime(time.time())[:6]
+        info = zipfile.ZipInfo(filename)
+        info.date_time = now
+        info.compress_type = zipfile.ZIP_DEFLATED
+        file.writestr(info, data)
+
+    def _zip_read(self, file, filename):
+        "Get the data from a file in the zip archive by filename"
+        file = zipfile.ZipFile(file, "r")
+        data = file.read(filename)
+        # Need to close the file
+        file.close()
+        return data
+
+    def _ods_content(self):
+        "Generate ods content.xml data"
+        # This will list all of the sheets in the document
+        self.sheetdata = ['tag', 'office:spreadsheet']
+        for sheet in self.sheets:
+            if self.debug:    
+                sheet_name = sheet.get_name()
+                print("    Creating Sheet '%s'" % sheet_name)
+            sheet_list = sheet.get_lists()
+            self.sheetdata.append(sheet_list)
+        # Automatic Styles
+        self.automatic_styles = self.styles.get_automatic_styles()
+        self.data = ['tag', 'office:document-content',
+          ['element', 'xmlns:office', 'urn:oasis:names:tc:opendocument:xmlns:office:1.0'],
+          ['element', 'xmlns:style', 'urn:oasis:names:tc:opendocument:xmlns:style:1.0'],
+          ['element', 'xmlns:text', 'urn:oasis:names:tc:opendocument:xmlns:text:1.0'],
+          ['element', 'xmlns:table', 'urn:oasis:names:tc:opendocument:xmlns:table:1.0'],
+          ['element', 'xmlns:draw', 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0'],
+          ['element', 'xmlns:fo', 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0'],
+          ['element', 'xmlns:xlink', 'http://www.w3.org/1999/xlink'],
+          ['element', 'xmlns:dc', 'http://purl.org/dc/elements/1.1/'],
+          ['element', 'xmlns:meta', 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0'],
+          ['element', 'xmlns:number', 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0'],
+          ['element', 'xmlns:svg', 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0'],
+          ['element', 'xmlns:chart', 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0'],
+          ['element', 'xmlns:dr3d', 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0'],
+          ['element', 'xmlns:math', 'http://www.w3.org/1998/Math/MathML'],
+          ['element', 'xmlns:form', 'urn:oasis:names:tc:opendocument:xmlns:form:1.0'],
+          ['element', 'xmlns:script', 'urn:oasis:names:tc:opendocument:xmlns:script:1.0'],
+          ['element', 'xmlns:ooo', 'http://openoffice.org/2004/office'],
+          ['element', 'xmlns:ooow', 'http://openoffice.org/2004/writer'],
+          ['element', 'xmlns:oooc', 'http://openoffice.org/2004/calc'],
+          ['element', 'xmlns:dom', 'http://www.w3.org/2001/xml-events'],
+          ['element', 'xmlns:xforms', 'http://www.w3.org/2002/xforms'],
+          ['element', 'xmlns:xsd', 'http://www.w3.org/2001/XMLSchema'],
+          ['element', 'xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance'],
+          ['element', 'office:version', '1.0'],
+          ['tagline', 'office:scripts'],
+          ['tag', 'office:font-face-decls',
+            ['tagline', 'style:font-face',
+              ['element', 'style:name', 'DejaVu Sans'],
+              ['element', 'svg:font-family', '&apos;DejaVu Sans&apos;'],
+              ['element', 'style:font-pitch', 'variable']],
+            ['tagline', 'style:font-face',
+              ['element', 'style:name', 'Nimbus Sans L'],
+              ['element', 'svg:font-family', '&apos;Nimbus Sans L&apos;'],
+              ['element', 'style:font-family-generic', 'swiss'],
+              ['element', 'style:font-pitch', 'variable']]],
+        # Automatic Styles
+        self.automatic_styles,
+          ['tag', 'office:body',
+            self.sheetdata]]                                      # Sheets are generated from the CalcSheet class
+        # Generate content.xml XML data
+        xml = XML()
+        self.lines = xml.convert(self.data)
+        self.filedata = '\n'.join(self.lines)
+        # Return generated data
+        return self.filedata
+
+    def _ods_manifest(self):
+        "Generate ods manifest.xml data"
+        self.data = ['tag', 'manifest:manifest',
+          ['element', 'xmlns:manifest', 'urn:oasis:names:tc:opendocument:xmlns:manifest:1.0'],
+          ['tagline', 'manifest:file-entry',
+            ['element', 'manifest:media-type', 'application/vnd.oasis.opendocument.spreadsheet'],
+            ['element', 'manifest:full-path', '/']],
+          ['tagline', 'manifest:file-entry',
+            ['element', 'manifest:media-type', 'application/vnd.sun.xml.ui.configuration'],
+            ['element', 'manifest:full-path', 'Configurations2/']],
+          ['tagline', 'manifest:file-entry',
+            ['element', 'manifest:media-type', ''],
+            ['element', 'manifest:full-path', 'Configurations2/accelerator/']],
+          ['tagline', 'manifest:file-entry',
+            ['element', 'manifest:media-type', ''],
+            ['element', 'manifest:full-path', 'Configurations2/accelerator/current.xml']],
+          ['tagline', 'manifest:file-entry',
+            ['element', 'manifest:media-type', 'text/xml'],
+            ['element', 'manifest:full-path', 'content.xml']],
+          ['tagline', 'manifest:file-entry',
+            ['element', 'manifest:media-type', 'text/xml'],
+            ['element', 'manifest:full-path', 'styles.xml']],
+          ['tagline', 'manifest:file-entry',
+            ['element', 'manifest:media-type', 'text/xml'],
+            ['element', 'manifest:full-path', 'meta.xml']],
+          ['tagline', 'manifest:file-entry',
+            ['element', 'manifest:media-type', 'text/xml'],
+            ['element', 'manifest:full-path', 'settings.xml']]]
+        # Add additional files to manifest list
+        for fileset in self.manifest_files:
+            (filename, filetype, newname) = fileset
+            addfile = ['tagline', 'manifest:file-entry',
+                   ['element', 'manifest:media-type', filetype],
+                   ['element', 'manifest:full-path', newname]]
+            self.data.append(addfile)
+        # Generate content.xml XML data
+        xml = XML()
+        self.lines = xml.convert(self.data)
+        self.filedata = '\n'.join(self.lines)
+        # Return generated data
+        return self.filedata
+
+    def _ods_settings(self):
+        "Generate ods settings.xml data"
+        self.data = ['tag', 'office:document-settings',
+          ['element', 'xmlns:office', 'urn:oasis:names:tc:opendocument:xmlns:office:1.0'],
+          ['element', 'xmlns:xlink', 'http://www.w3.org/1999/xlink'],
+          ['element', 'xmlns:config', 'urn:oasis:names:tc:opendocument:xmlns:config:1.0'],
+          ['element', 'xmlns:ooo', 'http://openoffice.org/2004/office'],
+          ['element', 'office:version', '1.0'],
+          ['tag', 'office:settings',
+            ['tag', 'config:config-item-set',
+              ['element', 'config:name', 'ooo:view-settings'],
+              ['tag', 'config:config-item',
+                ['element', 'config:name', 'VisibleAreaTop'],
+                ['element', 'config:type', 'int'],
+                ['data', '0']],
+              ['tag', 'config:config-item',
+                ['element', 'config:name', 'VisibleAreaLeft'],
+                ['element', 'config:type', 'int'],
+                ['data', '0']],
+              ['tag', 'config:config-item',
+                ['element', 'config:name', 'VisibleAreaWidth'],
+                ['element', 'config:type', 'int'],
+                ['data', '6774']],
+              ['tag', 'config:config-item',
+                ['element', 'config:name', 'VisibleAreaHeight'],
+                ['element', 'config:type', 'int'],
+                ['data', '2389']],
+              ['tag', 'config:config-item-map-indexed',
+                ['element', 'config:name', 'Views'],
+                ['tag', 'config:config-item-map-entry',
+                  ['tag', 'config:config-item',
+                    ['element', 'config:name', 'ViewId'],
+                    ['element', 'config:type', 'string'],
+                    ['data', 'View1']],
+                  ['tag', 'config:config-item-map-named',
+                    ['element', 'config:name', 'Tables'],
+                    ['tag', 'config:config-item-map-entry',
+                      ['element', 'config:name', 'Sheet1'],
+                      ['tag', 'config:config-item',
+                        ['element', 'config:name', 'CursorPositionX'], # Cursor Position A
+                        ['element', 'config:type', 'int'],
+                        ['data', '0']],
+                      ['tag', 'config:config-item',
+                        ['element', 'config:name', 'CursorPositionY'], # Cursor Position 1
+                        ['element', 'config:type', 'int'],
+                        ['data', '0']],
+                      ['tag', 'config:config-item',
+                        ['element', 'config:name', 'HorizontalSplitMode'],
+                        ['element', 'config:type', 'short'],
+                        ['data', '0']],
+                      ['tag', 'config:config-item',
+                        ['element', 'config:name', 'VerticalSplitMode'],
+                        ['element', 'config:type', 'short'],
+                        ['data', '0']],
+                      ['tag', 'config:config-item',
+                        ['element', 'config:name', 'HorizontalSplitPosition'],
+                        ['element', 'config:type', 'int'],
+                        ['data', '0']],
+                      ['tag', 'config:config-item',
+                        ['element', 'config:name', 'VerticalSplitPosition'],
+                        ['element', 'config:type', 'int'],
+                        ['data', '0']],
+                      ['tag', 'config:config-item',
+                        ['element', 'config:name', 'ActiveSplitRange'],
+                        ['element', 'config:type', 'short'],
+                        ['data', '2']],
+                      ['tag', 'config:config-item',
+                        ['element', 'config:name', 'PositionLeft'],
+                        ['element', 'config:type', 'int'],
+                        ['data', '0']],
+                      ['tag', 'config:config-item',
+                        ['element', 'config:name', 'PositionRight'],
+                        ['element', 'config:type', 'int'],
+                        ['data', '0']],
+                      ['tag', 'config:config-item',
+                        ['element', 'config:name', 'PositionTop'],
+                        ['element', 'config:type', 'int'],
+                        ['data', '0']],
+                      ['tag', 'config:config-item',
+                        ['element', 'config:name', 'PositionBottom'],
+                        ['element', 'config:type', 'int'],
+                        ['data', '0']]]],
+                  ['tag', 'config:config-item',
+                    ['element', 'config:name', 'ActiveTable'],
+                    ['element', 'config:type', 'string'],
+                    ['data', 'Sheet1']],
+                  ['tag', 'config:config-item',
+                    ['element', 'config:name', 'HorizontalScrollbarWidth'],
+                    ['element', 'config:type', 'int'],
+                    ['data', '270']],
+                  ['tag', 'config:config-item',
+                    ['element', 'config:name', 'ZoomType'],
+                    ['element', 'config:type', 'short'],
+                    ['data', '0']],
+                  ['tag', 'config:config-item',
+                    ['element', 'config:name', 'ZoomValue'],
+                    ['element', 'config:type', 'int'],
+                    ['data', '100']],
+                  ['tag', 'config:config-item',
+                    ['element', 'config:name', 'PageViewZoomValue'],
+                    ['element', 'config:type', 'int'],
+                    ['data', '60']],
+                  ['tag', 'config:config-item',
+                    ['element', 'config:name', 'ShowPageBreakPreview'],
+                    ['element', 'config:type', 'boolean'],
+                    ['data', 'false']],
+                  ['tag', 'config:config-item',
+                    ['element', 'config:name', 'ShowZeroValues'],
+                    ['element', 'config:type', 'boolean'],
+                    ['data', 'true']],
+                  ['tag', 'config:config-item',
+                    ['element', 'config:name', 'ShowNotes'],
+                    ['element', 'config:type', 'boolean'],
+                    ['data', 'true']],
+                  ['tag', 'config:config-item',
+                    ['element', 'config:name', 'ShowGrid'],
+                    ['element', 'config:type', 'boolean'],
+                    ['data', 'true']],
+                  ['tag', 'config:config-item',
+                    ['element', 'config:name', 'GridColor'],
+                    ['element', 'config:type', 'long'],
+                    ['data', '12632256']],
+                  ['tag', 'config:config-item',
+                    ['element', 'config:name', 'ShowPageBreaks'],
+                    ['element', 'config:type', 'boolean'],
+                    ['data', 'true']],
+                  ['tag', 'config:config-item',
+                    ['element', 'config:name', 'HasColumnRowHeaders'],
+                    ['element', 'config:type', 'boolean'],
+                    ['data', 'true']],
+                  ['tag', 'config:config-item',
+                    ['element', 'config:name', 'HasSheetTabs'],
+                    ['element', 'config:type', 'boolean'],
+                    ['data', 'true']],
+                  ['tag', 'config:config-item',
+                    ['element', 'config:name', 'IsOutlineSymbolsSet'],
+                    ['element', 'config:type', 'boolean'],
+                    ['data', 'true']],
+                  ['tag', 'config:config-item',
+                    ['element', 'config:name', 'IsSnapToRaster'],
+                    ['element', 'config:type', 'boolean'],
+                    ['data', 'false']],
+                  ['tag', 'config:config-item',
+                    ['element', 'config:name', 'RasterIsVisible'],
+                    ['element', 'config:type', 'boolean'],
+                    ['data', 'false']],
+                  ['tag', 'config:config-item',
+                    ['element', 'config:name', 'RasterResolutionX'],
+                    ['element', 'config:type', 'int'],
+                    ['data', '1270']],
+                  ['tag', 'config:config-item',
+                    ['element', 'config:name', 'RasterResolutionY'],
+                    ['element', 'config:type', 'int'],
+                    ['data', '1270']],
+                  ['tag', 'config:config-item',
+                    ['element', 'config:name', 'RasterSubdivisionX'],
+                    ['element', 'config:type', 'int'],
+                    ['data', '1']],
+                  ['tag', 'config:config-item',
+                    ['element', 'config:name', 'RasterSubdivisionY'],
+                    ['element', 'config:type', 'int'],
+                    ['data', '1']],
+                  ['tag', 'config:config-item',
+                    ['element', 'config:name', 'IsRasterAxisSynchronized'],
+                    ['element', 'config:type', 'boolean'],
+                    ['data', 'true']]]]],
+            ['tag', 'config:config-item-set',
+              ['element', 'config:name', 'ooo:configuration-settings'],
+              ['tag', 'config:config-item',
+                ['element', 'config:name', 'ShowZeroValues'],
+                ['element', 'config:type', 'boolean'],
+                ['data', 'true']],
+              ['tag', 'config:config-item',
+                ['element', 'config:name', 'ShowNotes'],
+                ['element', 'config:type', 'boolean'],
+                ['data', 'true']],
+              ['tag', 'config:config-item',
+                ['element', 'config:name', 'ShowGrid'],
+                ['element', 'config:type', 'boolean'],
+                ['data', 'true']],
+              ['tag', 'config:config-item',
+                ['element', 'config:name', 'GridColor'],
+                ['element', 'config:type', 'long'],
+                ['data', '12632256']],
+              ['tag', 'config:config-item',
+                ['element', 'config:name', 'ShowPageBreaks'],
+                ['element', 'config:type', 'boolean'],
+                ['data', 'true']],
+              ['tag', 'config:config-item',
+                ['element', 'config:name', 'LinkUpdateMode'],
+                ['element', 'config:type', 'short'],
+                ['data', '3']],
+              ['tag', 'config:config-item',
+                ['element', 'config:name', 'HasColumnRowHeaders'],
+                ['element', 'config:type', 'boolean'],
+                ['data', 'true']],
+              ['tag', 'config:config-item',
+                ['element', 'config:name', 'HasSheetTabs'],
+                ['element', 'config:type', 'boolean'],
+                ['data', 'true']],
+              ['tag', 'config:config-item',
+                ['element', 'config:name', 'IsOutlineSymbolsSet'],
+                ['element', 'config:type', 'boolean'],
+                ['data', 'true']],
+              ['tag', 'config:config-item',
+                ['element', 'config:name', 'IsSnapToRaster'],
+                ['element', 'config:type', 'boolean'],
+                ['data', 'false']],
+              ['tag', 'config:config-item',
+                ['element', 'config:name', 'RasterIsVisible'],
+                ['element', 'config:type', 'boolean'],
+                ['data', 'false']],
+              ['tag', 'config:config-item',
+                ['element', 'config:name', 'RasterResolutionX'],
+                ['element', 'config:type', 'int'],
+                ['data', '1270']],
+              ['tag', 'config:config-item',
+                ['element', 'config:name', 'RasterResolutionY'],
+                ['element', 'config:type', 'int'],
+                ['data', '1270']],
+              ['tag', 'config:config-item',
+                ['element', 'config:name', 'RasterSubdivisionX'],
+                ['element', 'config:type', 'int'],
+                ['data', '1']],
+              ['tag', 'config:config-item',
+                ['element', 'config:name', 'RasterSubdivisionY'],
+                ['element', 'config:type', 'int'],
+                ['data', '1']],
+              ['tag', 'config:config-item',
+                ['element', 'config:name', 'IsRasterAxisSynchronized'],
+                ['element', 'config:type', 'boolean'],
+                ['data', 'true']],
+              ['tag', 'config:config-item',
+                ['element', 'config:name', 'AutoCalculate'],
+                ['element', 'config:type', 'boolean'],
+                ['data', 'true']],
+              ['tag', 'config:config-item',
+                ['element', 'config:name', 'PrinterName'],
+                ['element', 'config:type', 'string'],
+                ['data', 'Generic Printer']],
+              ['tag', 'config:config-item',
+                ['element', 'config:name', 'PrinterSetup'],
+                ['element', 'config:type', 'base64Binary'],
+                ['data', 'YgH+/0dlbmVyaWMgUHJpbnRlcgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU0dFTlBSVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWAAMAqAAAAAAA//8FAFZUAAAkbQAASm9iRGF0YSAxCnByaW50ZXI9R2VuZXJpYyBQcmludGVyCm9yaWVudGF0aW9uPVBvcnRyYWl0CmNvcGllcz0xCnNjYWxlPTEwMAptYXJnaW5kYWp1c3RtZW50PTAsMCwwLDAKY29sb3JkZXB0aD0yNApwc2xldmVsPTAKY29sb3JkZXZpY2U9MApQUERDb250ZXhEYXRhClBhZ2VTaXplOkxldHRlcgAA']],
+              ['tag', 'config:config-item',
+                ['element', 'config:name', 'ApplyUserData'],
+                ['element', 'config:type', 'boolean'],
+                ['data', 'true']],
+              ['tag', 'config:config-item',
+                ['element', 'config:name', 'CharacterCompressionType'],
+                ['element', 'config:type', 'short'],
+                ['data', '0']],
+              ['tag', 'config:config-item',
+                ['element', 'config:name', 'IsKernAsianPunctuation'],
+                ['element', 'config:type', 'boolean'],
+                ['data', 'false']],
+              ['tag', 'config:config-item',
+                ['element', 'config:name', 'SaveVersionOnClose'],
+                ['element', 'config:type', 'boolean'],
+                ['data', 'false']],
+              ['tag', 'config:config-item',
+                ['element', 'config:name', 'UpdateFromTemplate'],
+                ['element', 'config:type', 'boolean'],
+                ['data', 'false']],
+              ['tag', 'config:config-item',
+                ['element', 'config:name', 'AllowPrintJobCancel'],
+                ['element', 'config:type', 'boolean'],
+                ['data', 'true']],
+              ['tag', 'config:config-item',
+                ['element', 'config:name', 'LoadReadonly'],
+                ['element', 'config:type', 'boolean'],
+                ['data', 'false']]]]]
+        # Generate content.xml XML data
+        xml = XML()
+        self.lines = xml.convert(self.data)
+        self.filedata = '\n'.join(self.lines)
+        # Return generated data
+        return self.filedata
+
+    def _ods_styles(self):
+        "Generate ods styles.xml data"
+        self.data = ['tag', 'office:document-styles',
+          ['element', 'xmlns:office', 'urn:oasis:names:tc:opendocument:xmlns:office:1.0'],
+          ['element', 'xmlns:style', 'urn:oasis:names:tc:opendocument:xmlns:style:1.0'],
+          ['element', 'xmlns:text', 'urn:oasis:names:tc:opendocument:xmlns:text:1.0'],
+          ['element', 'xmlns:table', 'urn:oasis:names:tc:opendocument:xmlns:table:1.0'],
+          ['element', 'xmlns:draw', 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0'],
+          ['element', 'xmlns:fo', 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0'],
+          ['element', 'xmlns:xlink', 'http://www.w3.org/1999/xlink'],
+          ['element', 'xmlns:dc', 'http://purl.org/dc/elements/1.1/'],
+          ['element', 'xmlns:meta', 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0'],
+          ['element', 'xmlns:number', 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0'],
+          ['element', 'xmlns:svg', 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0'],
+          ['element', 'xmlns:chart', 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0'],
+          ['element', 'xmlns:dr3d', 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0'],
+          ['element', 'xmlns:math', 'http://www.w3.org/1998/Math/MathML'],
+          ['element', 'xmlns:form', 'urn:oasis:names:tc:opendocument:xmlns:form:1.0'],
+          ['element', 'xmlns:script', 'urn:oasis:names:tc:opendocument:xmlns:script:1.0'],
+          ['element', 'xmlns:ooo', 'http://openoffice.org/2004/office'],
+          ['element', 'xmlns:ooow', 'http://openoffice.org/2004/writer'],
+          ['element', 'xmlns:oooc', 'http://openoffice.org/2004/calc'],
+          ['element', 'xmlns:dom', 'http://www.w3.org/2001/xml-events'],
+          ['element', 'office:version', '1.0'],
+          ['tag', 'office:font-face-decls',
+            ['tagline', 'style:font-face',
+              ['element', 'style:name', 'DejaVu Sans'],
+              ['element', 'svg:font-family', '&apos;DejaVu Sans&apos;'],
+              ['element', 'style:font-pitch', 'variable']],
+            ['tagline', 'style:font-face',
+              ['element', 'style:name', 'Nimbus Sans L'],
+              ['element', 'svg:font-family', '&apos;Nimbus Sans L&apos;'],
+              ['element', 'style:font-family-generic', 'swiss'],
+              ['element', 'style:font-pitch', 'variable']]],
+          ['tag', 'office:styles',
+            ['tag', 'style:default-style',
+              ['element', 'style:family', 'table-cell'],
+              ['tagline', 'style:table-cell-properties',
+                ['element', 'style:decimal-places', '2']],
+              ['tagline', 'style:paragraph-properties',
+                ['element', 'style:tab-stop-distance', '0.5in']],
+              ['tagline', 'style:text-properties',
+                ['element', 'style:font-name', 'Nimbus Sans L'],
+                ['element', 'fo:language', 'en'],
+                ['element', 'fo:country', 'US'],
+                ['element', 'style:font-name-asian', 'DejaVu Sans'],
+                ['element', 'style:language-asian', 'none'],
+                ['element', 'style:country-asian', 'none'],
+                ['element', 'style:font-name-complex', 'DejaVu Sans'],
+                ['element', 'style:language-complex', 'none'],
+                ['element', 'style:country-complex', 'none']]],
+            ['tag', 'number:number-style',
+              ['element', 'style:name', 'N0'],
+              ['tagline', 'number:number',
+                ['element', 'number:min-integer-digits', '1']]],
+            ['tag', 'number:currency-style',
+              ['element', 'style:name', 'N104P0'],
+              ['element', 'style:volatile', 'true'],
+              ['tag', 'number:currency-symbol',
+                ['element', 'number:language', 'en'],
+                ['element', 'number:country', 'US'],
+                ['data', '$']],
+              ['tagline', 'number:number',
+                ['element', 'number:decimal-places', '2'],
+                ['element', 'number:min-integer-digits', '1'],
+                ['element', 'number:grouping', 'true']]],
+            ['tag', 'number:currency-style',
+              ['element', 'style:name', 'N104'],
+              ['tagline', 'style:text-properties',
+                ['element', 'fo:color', '#ff0000']],
+              ['tag', 'number:text',
+                ['data', '-']],
+              ['tag', 'number:currency-symbol',
+                ['element', 'number:language', 'en'],
+                ['element', 'number:country', 'US'],
+                ['data', '$']],
+              ['tagline', 'number:number', 
+                ['element', 'number:decimal-places', '2'],
+                ['element', 'number:min-integer-digits', '1'],
+                ['element', 'number:grouping', 'true']],
+              ['tagline', 'style:map',
+                ['element', 'style:condition', 'value()&gt;=0'],
+                ['element', 'style:apply-style-name', 'N104P0']]],
+            ['tagline', 'style:style',
+              ['element', 'style:name', 'Default'],
+              ['element', 'style:family', 'table-cell']],
+            ['tag', 'style:style',
+              ['element', 'style:name', 'Result'],
+              ['element', 'style:family', 'table-cell'],
+              ['element', 'style:parent-style-name', 'Default'],
+              ['tagline', 'style:text-properties',
+                ['element', 'fo:font-style', 'italic'],
+                ['element', 'style:text-underline-style', 'solid'],
+                ['element', 'style:text-underline-width', 'auto'],
+                ['element', 'style:text-underline-color', 'font-color'],
+                ['element', 'fo:font-weight', 'bold']]],
+            ['tagline', 'style:style',
+              ['element', 'style:name', 'Result2'],
+              ['element', 'style:family', 'table-cell'],
+              ['element', 'style:parent-style-name', 'Result'],
+              ['element', 'style:data-style-name', 'N104']],
+            ['tag', 'style:style',
+              ['element', 'style:name', 'Heading'],
+              ['element', 'style:family', 'table-cell'],
+              ['element', 'style:parent-style-name', 'Default'],
+              ['tagline', 'style:table-cell-properties',
+                ['element', 'style:text-align-source', 'fix'],
+                ['element', 'style:repeat-content', 'false']],
+              ['tagline', 'style:paragraph-properties',
+                ['element', 'fo:text-align', 'center']],
+              ['tagline', 'style:text-properties',
+                ['element', 'fo:font-size', '16pt'],
+                ['element', 'fo:font-style', 'italic'],
+                ['element', 'fo:font-weight', 'bold']]],
+            ['tag', 'style:style',
+              ['element', 'style:name', 'Heading1'],
+              ['element', 'style:family', 'table-cell'],
+              ['element', 'style:parent-style-name', 'Heading'],
+              ['tagline', 'style:table-cell-properties',
+                ['element', 'style:rotation-angle', '90']]]],
+          ['tag', 'office:automatic-styles',
+            ['tag', 'style:page-layout',
+              ['element', 'style:name', 'pm1'],
+              ['tagline', 'style:page-layout-properties',
+                ['element', 'style:writing-mode', 'lr-tb']],
+              ['tag', 'style:header-style',
+                ['tagline', 'style:header-footer-properties',
+                  ['element', 'fo:min-height', '0.2957in'],
+                  ['element', 'fo:margin-left', '0in'],
+                  ['element', 'fo:margin-right', '0in'],
+                  ['element', 'fo:margin-bottom', '0.0984in']]],
+              ['tag', 'style:footer-style',
+                ['tagline', 'style:header-footer-properties',
+                  ['element', 'fo:min-height', '0.2957in'],
+                  ['element', 'fo:margin-left', '0in'],
+                  ['element', 'fo:margin-right', '0in'],
+                  ['element', 'fo:margin-top', '0.0984in']]]],
+            ['tag', 'style:page-layout',
+              ['element', 'style:name', 'pm2'],
+              ['tagline', 'style:page-layout-properties',
+                ['element', 'style:writing-mode', 'lr-tb']],
+              ['tag', 'style:header-style',
+                ['tag', 'style:header-footer-properties',
+                  ['element', 'fo:min-height', '0.2957in'],
+                  ['element', 'fo:margin-left', '0in'],
+                  ['element', 'fo:margin-right', '0in'],
+                  ['element', 'fo:margin-bottom', '0.0984in'],
+                  ['element', 'fo:border', '0.0346in solid #000000'],
+                  ['element', 'fo:padding', '0.0071in'],
+                  ['element', 'fo:background-color', '#c0c0c0'],
+                  ['tagline', 'style:background-image']]],
+              ['tag', 'style:footer-style',
+                ['tag', 'style:header-footer-properties',
+                  ['element', 'fo:min-height', '0.2957in'],
+                  ['element', 'fo:margin-left', '0in'],
+                  ['element', 'fo:margin-right', '0in'],
+                  ['element', 'fo:margin-top', '0.0984in'],
+                  ['element', 'fo:border', '0.0346in solid #000000'],
+                  ['element', 'fo:padding', '0.0071in'],
+                  ['element', 'fo:background-color', '#c0c0c0'],
+                  ['tagline', 'style:background-image']]]]],
+          ['tag', 'office:master-styles',
+            ['tag', 'style:master-page',
+              ['element', 'style:name', 'Default'],
+              ['element', 'style:page-layout-name', 'pm1'],
+              ['tag', 'style:header',
+                ['tag', 'text:p',
+                  ['data', '<text:sheet-name>???</text:sheet-name>']]],
+              ['tagline', 'style:header-left',
+                ['element', 'style:display', 'false']],
+              ['tag', 'style:footer',
+                ['tag', 'text:p',
+                  ['data', 'Page <text:page-number>1</text:page-number>']]],
+              ['tagline', 'style:footer-left',
+                ['element', 'style:display', 'false']]],
+            ['tag', 'style:master-page',
+              ['element', 'style:name', 'Report'],
+              ['element', 'style:page-layout-name', 'pm2'],
+              ['tag', 'style:header',
+                ['tag', 'style:region-left',
+                  ['tag', 'text:p',
+                    ['data', '<text:sheet-name>???</text:sheet-name> (<text:title>???</text:title>)']]],
+                ['tag', 'style:region-right',
+                  ['tag', 'text:p',
+                    ['data', '<text:date style:data-style-name="N2" text:date-value="2006-09-29">09/29/2006</text:date>, <text:time>13:02:56</text:time>']]]],
+              ['tagline', 'style:header-left',
+                ['element', 'style:display', 'false']],
+              ['tag', 'style:footer',
+                ['tag', 'text:p',
+                  ['data', 'Page <text:page-number>1</text:page-number> / <text:page-count>99</text:page-count>']]],
+              ['tagline', 'style:footer-left',
+                ['element', 'style:display', 'false']]]]]
+        # Generate content.xml XML data
+        xml = XML()
+        self.lines = xml.convert(self.data)
+        self.filedata = '\n'.join(self.lines)
+        # Return generated data
+        return self.filedata
 
 class Writer:
-       "Writer Class - Used to create OpenDocument Format Writer Documents."
-       def __init__(self):
-               "Initialize ooolib Writer instance"
-               # Default to no debugging
-               self.debug = False
-               self.meta = Meta('odt')
-
-       def set_meta(self, metaname, value):
-               "Set meta data in your document."
-               self.meta.set_meta(metaname, value)
-
-       def save(self, filename):
-               """Save .odt document
-
-               The save function saves the current .odt document.
-               """
-               if self.debug: print "Writing %s" % filename
-               self.savefile = zipfile.ZipFile(filename, "w")
-               if self.debug: print "  meta.xml"
-               self._zip_insert(self.savefile, "meta.xml", self.meta.get_meta())
-               if self.debug: print "  mimetype"
-               self._zip_insert(self.savefile, "mimetype", "application/vnd.oasis.opendocument.text")
-               if self.debug: print "  META-INF/manifest.xml"
-               self._zip_insert(self.savefile, "META-INF/manifest.xml", self._odt_manifest())
-               if self.debug: print "  content.xml"
-               self._zip_insert(self.savefile, "content.xml", self._odt_content())
-               if self.debug: print "  settings.xml"
-               # self._zip_insert(self.savefile, "settings.xml", self._odt_settings())
-               if self.debug: print "  styles.xml"
-               # self._zip_insert(self.savefile, "styles.xml", self._odt_styles())
-
-               # We need to close the file now that we are done creating it.
-               self.savefile.close()
-
-       def _zip_insert(self, file, filename, data):
-               now = time.localtime(time.time())[:6]
-               info = zipfile.ZipInfo(filename)
-               info.date_time = now
-               info.compress_type = zipfile.ZIP_DEFLATED
-               file.writestr(info, data)
-
-       def _odt_manifest(self):
-               "Generate odt manifest.xml data"
-
-               self.data = ['tag', 'manifest:manifest',
-                 ['element', 'xmlns:manifest', 'urn:oasis:names:tc:opendocument:xmlns:manifest:1.0'],
-                 ['tagline', 'manifest:file-entry',
-                   ['element', 'manifest:media-type', 'application/vnd.oasis.opendocument.text'],
-                   ['element', 'manifest:full-path', '/']],
-                 ['tagline', 'manifest:file-entry',
-                   ['element', 'manifest:media-type', 'text/xml'],
-                   ['element', 'manifest:full-path', 'content.xml']],
-                 ['tagline', 'manifest:file-entry',
-                   ['element', 'manifest:media-type', 'text/xml'],
-                   ['element', 'manifest:full-path', 'styles.xml']],
-                 ['tagline', 'manifest:file-entry',
-                   ['element', 'manifest:media-type', 'text/xml'],
-                   ['element', 'manifest:full-path', 'meta.xml']],
-                 ['tagline', 'manifest:file-entry',
-                   ['element', 'manifest:media-type', 'text/xml'],
-                   ['element', 'manifest:full-path', 'settings.xml']]]
-
-               # Generate content.xml XML data
-               xml = XML()
-               self.lines = xml.convert(self.data)
-               self.lines.insert(1, '<!DOCTYPE manifest:manifest PUBLIC "-//OpenOffice.org//DTD Manifest 1.0//EN" "Manifest.dtd">')
-               self.filedata = '\n'.join(self.lines)
-               # Return generated data
-               return self.filedata
-
-       def _odt_content(self):
-               "Generate odt content.xml data"
-
-               self.data = ['tag', 'office:document-content',
-                 ['element', 'xmlns:office', 'urn:oasis:names:tc:opendocument:xmlns:office:1.0'],
-                 ['element', 'xmlns:style', 'urn:oasis:names:tc:opendocument:xmlns:style:1.0'],
-                 ['element', 'xmlns:text', 'urn:oasis:names:tc:opendocument:xmlns:text:1.0'],
-                 ['element', 'xmlns:table', 'urn:oasis:names:tc:opendocument:xmlns:table:1.0'],
+    "Writer Class - Used to create OpenDocument Format Writer Documents."
+
+    def __init__(self):
+        "Initialize ooolib Writer instance"
+        # Default to no debugging
+        self.debug = False
+        self.meta = Meta('odt')
+
+    def set_meta(self, metaname, value):
+        "Set meta data in your document."
+        self.meta.set_meta(metaname, value)
+
+    def save(self, filename):
+        """Save .odt document
+
+        The save function saves the current .odt document.
+        """
+        if self.debug: print("Writing %s" % filename)
+        self.savefile = zipfile.ZipFile(filename, "w")
+        if self.debug: print("  meta.xml")
+        self._zip_insert(self.savefile, "meta.xml", self.meta.get_meta())
+        if self.debug: print("  mimetype")
+        self._zip_insert(self.savefile, "mimetype", "application/vnd.oasis.opendocument.text")
+        if self.debug: print("  META-INF/manifest.xml")
+        self._zip_insert(self.savefile, "META-INF/manifest.xml", self._odt_manifest())
+        if self.debug: print("  content.xml")
+        self._zip_insert(self.savefile, "content.xml", self._odt_content())
+        if self.debug: print("  settings.xml")
+        # self._zip_insert(self.savefile, "settings.xml", self._odt_settings())
+        if self.debug: print("  styles.xml")
+        # self._zip_insert(self.savefile, "styles.xml", self._odt_styles())
+        # We need to close the file now that we are done creating it.
+        self.savefile.close()
+
+    def _zip_insert(self, file, filename, data):
+        now = time.localtime(time.time())[:6]
+        info = zipfile.ZipInfo(filename)
+        info.date_time = now
+        info.compress_type = zipfile.ZIP_DEFLATED
+        file.writestr(info, data)
+
+    def _odt_manifest(self):
+        "Generate odt manifest.xml data"
+        self.data = ['tag', 'manifest:manifest',
+          ['element', 'xmlns:manifest', 'urn:oasis:names:tc:opendocument:xmlns:manifest:1.0'],
+          ['tagline', 'manifest:file-entry',
+            ['element', 'manifest:media-type', 'application/vnd.oasis.opendocument.text'],
+            ['element', 'manifest:full-path', '/']],
+          ['tagline', 'manifest:file-entry',
+            ['element', 'manifest:media-type', 'text/xml'],
+            ['element', 'manifest:full-path', 'content.xml']],
+          ['tagline', 'manifest:file-entry',
+            ['element', 'manifest:media-type', 'text/xml'],
+            ['element', 'manifest:full-path', 'styles.xml']],
+          ['tagline', 'manifest:file-entry',
+            ['element', 'manifest:media-type', 'text/xml'],
+            ['element', 'manifest:full-path', 'meta.xml']],
+          ['tagline', 'manifest:file-entry',
+            ['element', 'manifest:media-type', 'text/xml'],
+            ['element', 'manifest:full-path', 'settings.xml']]]
+        # Generate content.xml XML data
+        xml = XML()
+        self.lines = xml.convert(self.data)
+        self.lines.insert(1, '<!DOCTYPE manifest:manifest PUBLIC "-//OpenOffice.org//DTD Manifest 1.0//EN" "Manifest.dtd">')
+        self.filedata = '\n'.join(self.lines)
+        # Return generated data
+        return self.filedata
+
+    def _odt_content(self):
+        "Generate odt content.xml data"
+        self.data = ['tag', 'office:document-content',
+          ['element', 'xmlns:office', 'urn:oasis:names:tc:opendocument:xmlns:office:1.0'],
+          ['element', 'xmlns:style', 'urn:oasis:names:tc:opendocument:xmlns:style:1.0'],
+          ['element', 'xmlns:text', 'urn:oasis:names:tc:opendocument:xmlns:text:1.0'],
+          ['element', 'xmlns:table', 'urn:oasis:names:tc:opendocument:xmlns:table:1.0'],
                   ['element', 'xmlns:draw', 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0'],
-                 ['element', 'xmlns:fo', 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0'],
-                 ['element', 'xmlns:xlink', 'http://www.w3.org/1999/xlink'],
-                 ['element', 'xmlns:dc', 'http://purl.org/dc/elements/1.1/'],
-                 ['element', 'xmlns:meta', 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0'],
-                 ['element', 'xmlns:number', 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0'],
-                 ['element', 'xmlns:svg', 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0'],
-                 ['element', 'xmlns:chart', 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0'],
-                 ['element', 'xmlns:dr3d', 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0'],
-                 ['element', 'xmlns:math', 'http://www.w3.org/1998/Math/MathML'],
-                 ['element', 'xmlns:form', 'urn:oasis:names:tc:opendocument:xmlns:form:1.0'],
-                 ['element', 'xmlns:script', 'urn:oasis:names:tc:opendocument:xmlns:script:1.0'],
-                 ['element', 'xmlns:ooo', 'http://openoffice.org/2004/office'],
-                 ['element', 'xmlns:ooow', 'http://openoffice.org/2004/writer'],
-                 ['element', 'xmlns:oooc', 'http://openoffice.org/2004/calc'],
-                 ['element', 'xmlns:dom', 'http://www.w3.org/2001/xml-events'],
-                 ['element', 'xmlns:xforms', 'http://www.w3.org/2002/xforms'],
-                 ['element', 'xmlns:xsd', 'http://www.w3.org/2001/XMLSchema'],
-                 ['element', 'xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance'],
-                 ['element', 'office:version', '1.0'],
-                 ['tagline', 'office:scripts'],
-                 ['tag', 'office:font-face-decls',
-                   ['tagline', 'style:font-face',
-                     ['element', 'style:name', 'DejaVu Sans'],
-                     ['element', 'svg:font-family', '&apos;DejaVu Sans&apos;'],
-                     ['element', 'style:font-pitch', 'variable']],
+          ['element', 'xmlns:fo', 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0'],
+          ['element', 'xmlns:xlink', 'http://www.w3.org/1999/xlink'],
+          ['element', 'xmlns:dc', 'http://purl.org/dc/elements/1.1/'],
+          ['element', 'xmlns:meta', 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0'],
+          ['element', 'xmlns:number', 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0'],
+          ['element', 'xmlns:svg', 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0'],
+          ['element', 'xmlns:chart', 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0'],
+          ['element', 'xmlns:dr3d', 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0'],
+          ['element', 'xmlns:math', 'http://www.w3.org/1998/Math/MathML'],
+          ['element', 'xmlns:form', 'urn:oasis:names:tc:opendocument:xmlns:form:1.0'],
+          ['element', 'xmlns:script', 'urn:oasis:names:tc:opendocument:xmlns:script:1.0'],
+          ['element', 'xmlns:ooo', 'http://openoffice.org/2004/office'],
+          ['element', 'xmlns:ooow', 'http://openoffice.org/2004/writer'],
+          ['element', 'xmlns:oooc', 'http://openoffice.org/2004/calc'],
+          ['element', 'xmlns:dom', 'http://www.w3.org/2001/xml-events'],
+          ['element', 'xmlns:xforms', 'http://www.w3.org/2002/xforms'],
+          ['element', 'xmlns:xsd', 'http://www.w3.org/2001/XMLSchema'],
+          ['element', 'xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance'],
+          ['element', 'office:version', '1.0'],
+          ['tagline', 'office:scripts'],
+          ['tag', 'office:font-face-decls',
+            ['tagline', 'style:font-face',
+              ['element', 'style:name', 'DejaVu Sans'],
+              ['element', 'svg:font-family', '&apos;DejaVu Sans&apos;'],
+              ['element', 'style:font-pitch', 'variable']],
                     ['tagline', 'style:font-face',
-                     ['element', 'style:name', 'Nimbus Roman No9 L'],
-                     ['element', 'svg:font-family', '&apos;Nimbus Roman No9 L&apos;'],
-                     ['element', 'style:font-family-generic', 'roman'],
-                     ['element', 'style:font-pitch', 'variable']],
-                   ['tagline', 'style:font-face',
-                     ['element', 'style:name', 'Nimbus Sans L'],
-                     ['element', 'svg:font-family', '&apos;Nimbus Sans L&apos;'],
-                     ['element', 'style:font-family-generic', 'swiss'],
-                     ['element', 'style:font-pitch', 'variable']]],
-                 ['tagline', 'office:automatic-styles'],
-                 ['tag', 'office:body',
-                   ['tag', 'office:text',
-                     ['tagline', 'office:forms',
-                       ['element', 'form:automatic-focus', 'false'],
+              ['element', 'style:name', 'Nimbus Roman No9 L'],
+              ['element', 'svg:font-family', '&apos;Nimbus Roman No9 L&apos;'],
+              ['element', 'style:font-family-generic', 'roman'],
+              ['element', 'style:font-pitch', 'variable']],
+            ['tagline', 'style:font-face',
+              ['element', 'style:name', 'Nimbus Sans L'],
+              ['element', 'svg:font-family', '&apos;Nimbus Sans L&apos;'],
+              ['element', 'style:font-family-generic', 'swiss'],
+              ['element', 'style:font-pitch', 'variable']]],
+          ['tagline', 'office:automatic-styles'],
+          ['tag', 'office:body',
+            ['tag', 'office:text',
+              ['tagline', 'office:forms',
+                ['element', 'form:automatic-focus', 'false'],
                         ['element', 'form:apply-design-mode', 'false']],
-                     ['tag', 'text:sequence-decls',
-                       ['tagline', 'text:sequence-decl',
-                         ['element', 'text:display-outline-level', '0'],
-                         ['element', 'text:name', 'Illustration']],
-                       ['tagline', 'text:sequence-decl',
-                         ['element', 'text:display-outline-level', '0'],
+              ['tag', 'text:sequence-decls',
+                ['tagline', 'text:sequence-decl',
+                  ['element', 'text:display-outline-level', '0'],
+                  ['element', 'text:name', 'Illustration']],
+                ['tagline', 'text:sequence-decl',
+                  ['element', 'text:display-outline-level', '0'],
                           ['element', 'text:name', 'Table']],
-                       ['tagline', 'text:sequence-decl',
-                         ['element', 'text:display-outline-level', '0'],
-                         ['element', 'text:name', 'Text']],
-                       ['tagline', 'text:sequence-decl',
-                         ['element', 'text:display-outline-level', '0'],
-                         ['element', 'text:name', 'Drawing']]],
-                     ['tagline', 'text:p',
-                       ['element', 'text:style-name', 'Standard']]]]]
-
-               # Generate content.xml XML data
-               xml = XML()
-               self.lines = xml.convert(self.data)
-               self.filedata = '\n'.join(self.lines)
-               # Return generated data
-               return self.filedata
-
-
+                ['tagline', 'text:sequence-decl',
+                  ['element', 'text:display-outline-level', '0'],
+                  ['element', 'text:name', 'Text']],
+                ['tagline', 'text:sequence-decl',
+                  ['element', 'text:display-outline-level', '0'],
+                  ['element', 'text:name', 'Drawing']]],
+              ['tagline', 'text:p',
+                ['element', 'text:style-name', 'Standard']]]]]
+        # Generate content.xml XML data
+        xml = XML()
+        self.lines = xml.convert(self.data)
+        self.filedata = '\n'.join(self.lines)
+        # Return generated data
+        return self.filedata