1 "ooolib-python - Copyright (C) 2006-2009 Joseph Colton"
3 # ooolib-python - Python module for creating Open Document Format documents.
4 # Copyright (C) 2006-2009 Joseph Colton
6 # This library is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU Lesser General Public
8 # License as published by the Free Software Foundation; either
9 # version 2.1 of the License, or (at your option) any later version.
11 # This library is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 # Lesser General Public License for more details.
16 # You should have received a copy of the GNU Lesser General Public
17 # License along with this library; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 # You can contact me by email at josephcolton@gmail.com
22 # Import Standard Modules
23 import zipfile # Needed for reading/writing documents
29 import xml.parsers.expat # Needed for parsing documents
32 "Get the ooolib-python version number"
36 "Get the ooolib-python version"
37 return "ooolib-python-%s" % version_number()
39 def clean_string(data):
40 "Returns an XML friendly copy of the data string"
42 data = unicode(data) # This line thanks to Chris Ender
44 data = data.replace('&', '&')
45 data = data.replace("'", ''')
46 data = data.replace('"', '"')
47 data = data.replace('<', '<')
48 data = data.replace('>', '>')
49 data = data.replace('\t', '<text:tab-stop/>')
50 data = data.replace('\n', '<text:line-break/>')
54 "XML Class - Used to convert nested lists into XML"
56 "Initialize ooolib XML instance"
59 def _xmldata(self, data):
60 datatype = data.pop(0)
61 datavalue = data.pop(0)
62 outstring = '%s' % datavalue
65 def _xmltag(self, data):
68 datatype = data.pop(0)
69 dataname = data.pop(0)
70 outstring = '<%s' % dataname
76 if (newdata[0] == 'element' and element):
77 newstring = self._xmlelement(newdata)
78 outstring = '%s %s' % (outstring, newstring)
80 if (newdata[0] != 'element' and element):
82 outstring = '%s>' % outstring
83 if (newdata[0] == 'tag' or newdata[0] == 'tagline'):
84 outstring = '%s\n' % outstring
85 if (newdata[0] == 'tag'):
86 newstring = self._xmltag(newdata)
87 outstring = '%s%s' % (outstring, newstring)
89 if (newdata[0] == 'tagline'):
90 newstring = self._xmltagline(newdata)
91 outstring = '%s%s' % (outstring, newstring)
93 if (newdata[0] == 'data'):
94 newstring = self._xmldata(newdata)
95 outstring = '%s%s' % (outstring, newstring)
99 outstring = '%s>\n' % outstring
100 outstring = '%s</%s>\n' % (outstring, dataname)
103 def _xmltagline(self, data):
106 datatype = data.pop(0)
107 dataname = data.pop(0)
108 outstring = '<%s' % dataname
112 newdata = data.pop(0)
113 if (newdata[0] != 'element'): break
114 newstring = self._xmlelement(newdata)
115 outstring = '%s %s' % (outstring, newstring)
116 outstring = '%s/>\n' % outstring
117 # Non-Element Section should not exist
120 def _xmlelement(self, data):
121 datatype = data.pop(0)
122 dataname = data.pop(0)
123 datavalue = data.pop(0)
124 outstring = '%s="%s"' % (dataname, datavalue)
127 def convert(self, data):
128 """Convert nested lists into XML
130 The convert method takes a nested lists and converts them
131 into XML to be used in Open Document Format documents.
132 There are three types of lists that are recognized at this
133 time. They are as follows:
135 'tag' - Tag opens a set of data that is eventually closed
140 'tagline' - Taglines are similar to tags, except they open
141 and close themselves.
142 List: ['tagline', 'xml']
145 'element' - Elements are pieces of information stored in an
146 opening tag or tagline.
147 List: ['element', 'color', 'blue']
150 'data' - Data is plain text directly inserted into the XML
152 List: ['data', 'hello']
155 Bring them all together for something like this.
158 ['tag', 'xml', ['element', 'a', 'b'], ['tagline', 'xml2'],
162 <xml a="b"><xml2/>asdf</xml>
165 outlines.append('<?xml version="1.0" encoding="UTF-8"?>')
166 if (type(data) == type([]) and len(data) > 0):
167 if data[0] == 'tag': outlines.append(self._xmltag(data))
173 def __init__(self, doctype, debug=False):
174 self.doctype = doctype
179 # The generator should always default to the version number
180 self.meta_generator = version()
182 self.meta_subject = ''
183 self.meta_description = ''
184 self.meta_keywords = []
185 self.meta_creator = 'ooolib-python'
186 self.meta_editor = ''
187 self.meta_user1_name = 'Info 1'
188 self.meta_user2_name = 'Info 2'
189 self.meta_user3_name = 'Info 3'
190 self.meta_user4_name = 'Info 4'
191 self.meta_user1_value = ''
192 self.meta_user2_value = ''
193 self.meta_user3_value = ''
194 self.meta_user4_value = ''
195 self.meta_creation_date = self.meta_time()
198 self.parser_element_list = []
199 self.parser_element = ""
200 self.parser_count = 0
202 def set_meta(self, metaname, value):
203 """Set meta data in your document.
205 Currently implemented metaname options are as follows:
206 'creator' - The document author
208 if metaname == 'creator': self.meta_creator = value
209 if metaname == 'editor': self.meta_editor = value
210 if metaname == 'title': self.meta_title = value
211 if metaname == 'subject': self.meta_subject = value
212 if metaname == 'description': self.meta_description = value
213 if metaname == 'user1name': self.meta_user1_name = value
214 if metaname == 'user2name': self.meta_user2_name = value
215 if metaname == 'user3name': self.meta_user3_name = value
216 if metaname == 'user4name': self.meta_user4_name = value
217 if metaname == 'user1value': self.meta_user1_value = value
218 if metaname == 'user2value': self.meta_user2_value = value
219 if metaname == 'user3value': self.meta_user3_value = value
220 if metaname == 'user4value': self.meta_user4_value = value
221 if metaname == 'keyword':
222 if value not in self.meta_keywords:
223 self.meta_keywords.append(value)
225 def get_meta_value(self, metaname):
226 "Get meta data value for a given metaname."
228 if metaname == 'creator': return self.meta_creator
229 if metaname == 'editor': return self.meta_editor
230 if metaname == 'title': return self.meta_title
231 if metaname == 'subject': return self.meta_subject
232 if metaname == 'description': return self.meta_description
233 if metaname == 'user1name': return self.meta_user1_name
234 if metaname == 'user2name': return self.meta_user2_name
235 if metaname == 'user3name': return self.meta_user3_name
236 if metaname == 'user4name': return self.meta_user4_name
237 if metaname == 'user1value': return self.meta_user1_value
238 if metaname == 'user2value': return self.meta_user2_value
239 if metaname == 'user3value': return self.meta_user3_value
240 if metaname == 'user4value': return self.meta_user4_value
241 if metaname == 'keyword': return self.meta_keywords
244 "Return time string in meta data format"
246 stamp = "%04d-%02d-%02dT%02d:%02d:%02d" % (t[0], t[1], t[2], t[3], t[4], t[5])
249 def parse_start_element(self, name, attrs):
250 if self.debug: print '* Start element:', name
251 self.parser_element_list.append(name)
252 self.parser_element = self.parser_element_list[-1]
254 # Need the meta name from the user-defined tags
255 if (self.parser_element == "meta:user-defined"):
256 self.parser_count += 1
257 # Set user-defined name
258 self.set_meta("user%dname" % self.parser_count, attrs['meta:name'])
260 # Debugging statements
261 if self.debug: print " List: ", self.parser_element_list
262 if self.debug: print " Attributes: ", attrs
265 def parse_end_element(self, name):
266 if self.debug: print '* End element:', name
267 if name != self.parser_element:
268 print "Tag Mismatch: '%s' != '%s'" % (name, self.parser_element)
269 self.parser_element_list.pop()
271 # Readjust parser_element_list and parser_element
272 if (self.parser_element_list):
273 self.parser_element = self.parser_element_list[-1]
275 self.parser_element = ""
277 def parse_char_data(self, data):
278 if self.debug: print " Character data: ", repr(data)
280 # Collect Meta data fields
281 if (self.parser_element == "dc:title"):
282 self.set_meta("title", data)
283 if (self.parser_element == "dc:description"):
284 self.set_meta("description", data)
285 if (self.parser_element == "dc:subject"):
286 self.set_meta("subject", data)
287 if (self.parser_element == "meta:initial-creator"):
288 self.set_meta("creator", data)
290 # Try to maintain the same creation date
291 if (self.parser_element == "meta:creation-date"):
292 self.meta_creation_date = data
294 # The user defined fields need to be kept track of, parser_count does that
295 if (self.parser_element == "meta:user-defined"):
296 self.set_meta("user%dvalue" % self.parser_count, data)
298 def meta_parse(self, data):
299 "Parse Meta Data from a meta.xml file"
301 # Debugging statements
303 # Sometimes it helps to see the document that was read from
308 parser = xml.parsers.expat.ParserCreate()
309 # Set up parser callback functions
310 parser.StartElementHandler = self.parse_start_element
311 parser.EndElementHandler = self.parse_end_element
312 parser.CharacterDataHandler = self.parse_char_data
314 # Actually parse the data
315 parser.Parse(data, 1)
318 "Generate meta.xml file data"
319 self.meta_date = self.meta_time()
320 self.data = ['tag', 'office:document-meta',
321 ['element', 'xmlns:office', 'urn:oasis:names:tc:opendocument:xmlns:office:1.0'],
322 ['element', 'xmlns:xlink', 'http://www.w3.org/1999/xlink'],
323 ['element', 'xmlns:dc', 'http://purl.org/dc/elements/1.1/'],
324 ['element', 'xmlns:meta', 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0'],
325 ['element', 'xmlns:ooo', 'http://openoffice.org/2004/office'],
326 ['element', 'office:version', '1.0'],
327 ['tag', 'office:meta',
328 ['tag', 'meta:generator', # Was: 'OpenOffice.org/2.0$Linux OpenOffice.org_project/680m5$Build-9011'
329 ['data', self.meta_generator]], # Generator is set the the ooolib-python version.
331 ['data', self.meta_title]], # This data is the document title
332 ['tag', 'dc:description',
333 ['data', self.meta_description]], # This data is the document description
334 ['tag', 'dc:subject',
335 ['data', self.meta_subject]], # This data is the document subject
336 ['tag', 'meta:initial-creator',
337 ['data', self.meta_creator]], # This data is the document creator
338 ['tag', 'meta:creation-date',
339 ['data', self.meta_creation_date]], # This is the original creation date of the document
340 ['tag', 'dc:creator',
341 ['data', self.meta_editor]], # This data is the document editor
343 ['data', self.meta_date]], # This is the last modified date of the document
344 ['tag', 'dc:language',
345 ['data', 'en-US']], # We will probably always use en-US for language
346 ['tag', 'meta:editing-cycles',
347 ['data', '1']], # Edit cycles will probably always be 1 for generated documents
348 ['tag', 'meta:editing-duration',
349 ['data', 'PT0S']], # Editing duration is modified - creation date
350 ['tag', 'meta:user-defined',
351 ['element', 'meta:name', self.meta_user1_name],
352 ['data', self.meta_user1_value]],
353 ['tag', 'meta:user-defined',
354 ['element', 'meta:name', self.meta_user2_name],
355 ['data', self.meta_user2_value]],
356 ['tag', 'meta:user-defined',
357 ['element', 'meta:name', self.meta_user3_name],
358 ['data', self.meta_user3_value]],
359 ['tag', 'meta:user-defined',
360 ['element', 'meta:name', self.meta_user4_name],
361 ['data', self.meta_user4_value]]]]
362 # ['tagline', 'meta:document-statistic',
363 # ['element', 'meta:table-count', len(self.sheets)], # len(self.sheets) ?
364 # ['element', 'meta:cell-count', '15']]]] # Not sure how to keep track
366 # Generate content.xml XML data
368 self.lines = xml.convert(self.data)
369 self.filedata = '\n'.join(self.lines)
370 # Return generated data
376 "Calc Style Management - Used to keep track of created styles."
379 self.style_config = {}
382 self.style_column = 1
385 # Style Properties (Defaults) - To be used later
386 self.property_column_width_default = '0.8925in' # Default Column Width
387 self.property_row_height_default = '0.189in' # Default Row Height
389 self.property_column_width = '0.8925in' # Default Column Width
390 self.property_row_height = '0.189in' # Default Row Height
391 self.property_cell_bold = False # Bold off be default
392 self.property_cell_italic = False # Italic off be default
393 self.property_cell_underline = False # Underline off be default
394 self.property_cell_fg_color = 'default' # Text Color Default
395 self.property_cell_bg_color = 'default' # Cell Background Default
396 self.property_cell_bg_image = 'none' # Cell Background Default
397 self.property_cell_fontsize = '10' # Cell Font Size Default
398 self.property_cell_valign = 'default' # Vertial Alignment Default
399 self.property_cell_halign = 'default' # Horizantal Alignment Default
401 def get_next_style(self, style):
402 "Returns the next style code for the given style"
405 style_code = 'ta%d' % self.style_table
407 if style == 'column':
408 style_code = 'co%d' % self.style_column
411 style_code = 'ro%d' % self.style_row
414 style_code = 'ce%d' % self.style_cell
418 def set_property(self, style, name, value):
419 "Sets a property which will later be turned into a code"
422 if style == 'column':
423 if name == 'style:column-width': self.property_column_width = value
425 if name == 'style:row-height': self.property_row_height = value
427 if name == 'bold' and type(value) == type(True): self.property_cell_bold = value
428 if name == 'italic' and type(value) == type(True): self.property_cell_italic = value
429 if name == 'underline' and type(value) == type(True): self.property_cell_underline = value
430 if name == 'fontsize': self.property_cell_fontsize = value
432 self.property_cell_fg_color = 'default'
433 redata = re.search("^(#[\da-fA-F]{6})$", value)
434 if redata: self.property_cell_fg_color = value.lower()
435 if name == 'background':
436 self.property_cell_bg_color = 'default'
437 redata = re.search("^(#[\da-fA-F]{6})$", value)
438 if redata: self.property_cell_bg_color = value.lower()
439 if name == 'backgroundimage':
440 self.property_cell_bg_image = value
442 self.property_cell_valign = value
444 self.property_cell_halign = value
446 def get_style_code(self, style):
450 if style == 'column':
451 style_data = tuple([style,
452 ('style:column-width', self.property_column_width)])
453 if style_data in self.style_config:
454 # Style Exists, return code
455 style_code = self.style_config[style_data]
457 # Style does not exist, create code and return it
458 style_code = self.get_next_style(style)
459 self.style_config[style_data] = style_code
461 style_data = tuple([style,
462 ('style:row-height', self.property_row_height)])
463 if style_data in self.style_config:
464 # Style Exists, return code
465 style_code = self.style_config[style_data]
467 # Style does not exist, create code and return it
468 style_code = self.get_next_style(style)
469 self.style_config[style_data] = style_code
472 # Add additional styles
473 if self.property_cell_bold: style_data.append(('bold', True))
474 if self.property_cell_italic: style_data.append(('italic', True))
475 if self.property_cell_underline: style_data.append(('underline', True))
476 if self.property_cell_fontsize != '10':
477 style_data.append(('fontsize', self.property_cell_fontsize))
478 if self.property_cell_fg_color != 'default':
479 style_data.append(('color', self.property_cell_fg_color))
480 if self.property_cell_bg_color != 'default':
481 style_data.append(('background', self.property_cell_bg_color))
482 if self.property_cell_bg_image != 'none':
483 style_data.append(('backgroundimage', self.property_cell_bg_image))
484 if self.property_cell_valign != 'default':
485 style_data.append(('valign', self.property_cell_valign))
486 if self.property_cell_halign != 'default':
487 style_data.append(('halign', self.property_cell_halign))
489 style_data = tuple(style_data)
490 if style_data in self.style_config:
491 # Style Exists, return code
492 style_code = self.style_config[style_data]
494 # Style does not exist, create code and return it
495 style_code = self.get_next_style(style)
496 self.style_config[style_data] = style_code
499 def get_automatic_styles(self):
500 "Return 'office:automatic-styles' lists"
501 automatic_styles = ['tag', 'office:automatic-styles']
503 for style_data in self.style_config:
504 style_code = self.style_config[style_data]
505 style_data = list(style_data)
506 style = style_data.pop(0)
508 if style == 'column':
509 style_list = ['tag', 'style:style',
510 ['element', 'style:name', style_code], # Column 'co1' properties
511 ['element', 'style:family', 'table-column']]
512 tagline = ['tagline', 'style:table-column-properties',
513 ['element', 'fo:break-before', 'auto']] # unsure what break before means
515 for set in style_data:
517 if name == 'style:column-width':
518 tagline.append(['element', 'style:column-width', value])
519 style_list.append(tagline)
520 automatic_styles.append(style_list)
523 style_list = ['tag', 'style:style',
524 ['element', 'style:name', style_code], # Column 'ro1' properties
525 ['element', 'style:family', 'table-row']]
526 tagline = ['tagline', 'style:table-row-properties']
528 for set in style_data:
530 if name == 'style:row-height':
531 tagline.append(['element', 'style:row-height', value])
532 tagline.append(['element', 'fo:break-before', 'auto'])
533 # tagline.append(['element', 'style:use-optimal-row-height', 'true']) # Overrides settings
534 style_list.append(tagline)
535 automatic_styles.append(style_list)
538 style_list = ['tag', 'style:style',
539 ['element', 'style:name', style_code], # ce1 style
540 ['element', 'style:family', 'table-cell'], # cell
541 ['element', 'style:parent-style-name', 'Default']] # parent is Default
544 tagline = ['tag', 'style:table-cell-properties']
545 tagline_additional = []
546 for set in style_data:
548 if name == 'background':
549 tagline.append(['element', 'fo:background-color', value])
550 if name == 'backgroundimage':
551 tagline.append(['element', 'fo:background-color', 'transparent'])
552 # Additional tags added later
553 bgimagetag = ['tagline', 'style:background-image']
554 bgimagetag.append(['element', 'xlink:href', value])
555 bgimagetag.append(['element', 'xlink:type', 'simple'])
556 bgimagetag.append(['element', 'xlink:actuate', 'onLoad'])
557 tagline_additional.append(bgimagetag)
559 if value in ['top', 'bottom', 'middle']:
560 tagline.append(['element', 'style:vertical-align', value])
562 tagline.append(['element', 'style:text-align-source', 'fix'])
563 if value in ['filled']:
564 tagline.append(['element', 'style:repeat-content', 'true'])
566 tagline.append(['element', 'style:repeat-content', 'false'])
568 # Add any additional internal tags
569 while tagline_additional:
570 tagadd = tagline_additional.pop(0)
571 tagline.append(tagadd)
573 style_list.append(tagline)
575 # Paragraph Properties
576 tagline = ['tagline', 'style:paragraph-properties']
577 tagline_valid = False
578 for set in style_data:
582 if value in ['center']:
583 tagline.append(['element', 'fo:text-align', 'center'])
584 if value in ['end', 'right']:
585 tagline.append(['element', 'fo:text-align', 'end'])
586 if value in ['start', 'filled', 'left']:
587 tagline.append(['element', 'fo:text-align', 'start'])
588 if value in ['justify']:
589 tagline.append(['element', 'fo:text-align', 'justify'])
590 # Conditionally add the tagline
591 if tagline_valid: style_list.append(tagline)
595 tagline = ['tagline', 'style:text-properties']
596 for set in style_data:
599 tagline.append(['element', 'fo:font-weight', 'bold'])
601 tagline.append(['element', 'fo:font-style', 'italic'])
602 if name == 'underline':
603 tagline.append(['element', 'style:text-underline-style', 'solid'])
604 tagline.append(['element', 'style:text-underline-width', 'auto'])
605 tagline.append(['element', 'style:text-underline-color', 'font-color'])
607 tagline.append(['element', 'fo:color', value])
608 if name == 'fontsize':
609 tagline.append(['element', 'fo:font-size', '%spt' % value])
610 style_list.append(tagline)
612 automatic_styles.append(style_list)
616 automatic_styles.append(['tag', 'style:style',
617 ['element', 'style:name', 'ta1'],
618 ['element', 'style:family', 'table'],
619 ['element', 'style:master-page-name', 'Default'],
620 ['tagline', 'style:table-properties',
621 ['element', 'table:display', 'true'],
622 ['element', 'style:writing-mode', 'lr-tb']]])
625 return automatic_styles
630 "Calc Sheet Class - Used to keep track of the data for an individual sheet."
632 def __init__(self, sheetname):
634 self.sheet_name = sheetname
635 self.sheet_values = {}
636 self.sheet_config = {}
640 def get_sheet_dimensions(self):
641 "Returns the max column and row"
642 return (self.max_col, self.max_row)
644 def clean_formula(self, data):
645 "Returns a formula for use in ODF"
646 # Example Translations
648 # datavalue = 'oooc:=SUM([.A1:.A2])'
649 # '=IF((A5>A4);A4;"")'
650 # datavalue = 'oooc:=IF(([.A5]>[.A4]);[.A4];"")'
652 data = clean_string(data)
653 redata = re.search('^=([A-Z]+)(\(.*)$', data)
655 # funct is the function name. The rest if the string will be the functArgs
656 funct = redata.group(1)
657 functArgs = redata.group(2)
658 # Search for cell lebels and replace them
659 reList = re.findall('([A-Z]+\d+)', functArgs)
660 # sort and keep track so we do not do a cell more than once
664 # Replace each cell label
665 curVar = reList.pop()
666 if curVar == lastVar: continue
668 functArgs = functArgs.replace(curVar, '[.%s]' % curVar)
669 data = 'oooc:=%s%s' % (funct, functArgs)
673 "Returns the sheet name"
674 return self.sheet_name
676 def set_name(self, sheetname):
677 "Resets the sheet name"
678 self.sheet_name = sheetname
680 def get_sheet_values(self):
681 "Returns the sheet cell values"
682 return self.sheet_values
684 def get_sheet_value(self, col, row):
685 "Get the value contents of a cell"
687 if cell in self.sheet_values:
688 return self.sheet_values[cell]
692 def get_sheet_config(self):
693 "Returns the sheet cell properties"
694 return self.sheet_config
696 def set_sheet_config(self, location, style_code):
697 "Sets Style Code for a given location"
698 self.sheet_config[location] = style_code
700 def set_sheet_value(self, cell, datatype, datavalue):
701 """Sets the value for a specific cell
703 cell must be in the format (col, row) where row and col are int.
704 Example: B5 would be written as (2, 5)
705 datatype must be one of 'string', 'float', 'formula'
706 datavalue should be a string
709 if type(cell) != type(()) or len(cell) != 2:
713 if type(col) != type(1):
716 if type(row) != type(1):
720 if datatype in ['string', 'annotation']:
721 datavalue = clean_string(datavalue)
722 # Fix Link Data. Link's value is a tuple containing (url, description)
723 if (datatype == 'link'):
724 url = clean_string(datavalue[0])
725 desc = clean_string(datavalue[1])
726 datavalue = (url, desc)
728 if datatype == 'formula':
729 datavalue = self.clean_formula(datavalue)
730 # Adjust maximum sizes
731 if col > self.max_col: self.max_col = col
732 if row > self.max_row: self.max_row = row
733 datatype = str(datatype)
734 if (datatype not in ['string', 'float', 'formula', 'annotation', 'link']):
735 # Set all unknown cell types to string
737 datavalue = str(datavalue)
739 # The following lines are taken directly from HPS
740 # self.sheet_values[cell] = (datatype, datavalue)
741 # HPS: Cell content is now a list of tuples instead of a tuple
742 # While storing here, store the cell contents first and the annotation next. While generating the XML reverse this
743 contents = self.sheet_values.get(cell, {'annotation':None,'link':None, 'value':None})
744 if datatype == 'annotation':
745 contents['annotation'] = (datatype, datavalue)
746 elif datatype == 'link':
747 contents['link'] = (datatype, datavalue)
749 contents['value'] = (datatype, datavalue)
751 self.sheet_values[cell] = contents
755 "Returns nested lists for XML processing"
756 if (self.max_col == 0 and self.max_row == 0):
757 sheet_lists = ['tag', 'table:table',
758 ['element', 'table:name', self.sheet_name], # Set the Sheet Name
759 ['element', 'table:style-name', 'ta1'],
760 ['element', 'table:print', 'false'],
761 ['tagline', 'table:table-column',
762 ['element', 'table:style-name', 'co1'],
763 ['element', 'table:default-cell-style-name', 'Default']],
764 ['tag', 'table:table-row',
765 ['element', 'table:style-name', 'ro1'],
766 ['tagline', 'table:table-cell']]]
769 sheet_lists = ['tag', 'table:table',
770 ['element', 'table:name', self.sheet_name], # Set the sheet name
771 ['element', 'table:style-name', 'ta1'],
772 ['element', 'table:print', 'false']]
774 # ['tagline', 'table:table-column',
775 # ['element', 'table:style-name', 'co1'],
776 # ['element', 'table:number-columns-repeated', self.max_col], # max_col? '2'
777 # ['element', 'table:default-cell-style-name', 'Default']],
779 # Need to add column information
780 for col in range(1, self.max_col+1):
781 location = ('col', col)
783 if location in self.sheet_config:
784 style_code = self.sheet_config[location]
785 sheet_lists.append(['tagline', 'table:table-column',
786 ['element', 'table:style-name', style_code],
787 ['element', 'table:default-cell-style-name', 'Default']])
790 # Need to create each row
791 for row in range(1, self.max_row + 1):
792 location = ('row', row)
794 if location in self.sheet_config:
795 style_code = self.sheet_config[location]
796 rowlist = ['tag', 'table:table-row',
797 ['element', 'table:style-name', style_code]]
798 for col in range(1, self.max_col + 1):
800 style_code = 'ce1' # Default all cells to ce1
801 if cell in self.sheet_config:
802 style_code = self.sheet_config[cell] # Lookup cell if available
803 if cell in self.sheet_values:
804 # (datatype, datavalue) = self.sheet_values[cell] # Marked for removal
805 collist = ['tag', 'table:table-cell']
806 if style_code != 'ce1':
807 collist.append(['element', 'table:style-name', style_code])
809 # Contents, annotations, and links added by HPS
810 contents = self.sheet_values[cell] # cell contents is a dictionary
811 if contents['value']:
812 (datatype, datavalue) = contents['value']
813 if datatype == 'float':
814 collist.append(['element', 'office:value-type', datatype])
815 collist.append(['element', 'office:value', datavalue])
817 if datatype == 'string':
818 collist.append(['element', 'office:value-type', datatype])
819 if datatype == 'formula':
820 collist.append(['element', 'table:formula', datavalue])
821 collist.append(['element', 'office:value-type', 'float'])
822 collist.append(['element', 'office:value', '0'])
827 if contents['annotation']:
828 (annotype, annoval) = contents['annotation']
829 collist.append(['tag', 'office:annotation',
830 ['tag', 'text:p', ['data', annoval]]])
833 (linktype, linkval) = contents['link']
835 collist.append(['tag', 'text:p', ['data', datavalue],
836 ['tag', 'text:a', ['element', 'xlink:href', linkval[0]],
837 ['data', linkval[1]]]])
838 else: # no value; just fill the link
839 collist.append(['tag', 'text:p',
840 ['tag', 'text:a', ['element', 'xlink:href', linkval[0]],
841 ['data', linkval[1]]]])
844 collist.append(['tag', 'text:p', ['data', datavalue]])
849 collist = ['tagline', 'table:table-cell']
850 rowlist.append(collist)
851 sheet_lists.append(rowlist)
855 "Calc Class - Used to create OpenDocument Format Calc Spreadsheets."
856 def __init__(self, sheetname=None, opendoc=None, debug=False):
857 "Initialize ooolib Calc instance"
858 # Default to no debugging
860 if not sheetname: sheetname = "Sheet1"
861 self.sheets = [CalcSheet(sheetname)] # The main sheet will be initially called 'Sheet1'
862 self.sheet_index = 0 # We initially start on the first sheet
863 self.styles = CalcStyles()
864 self.meta = Meta('ods')
865 self.styles.get_style_code('column') # Force generation of default column
866 self.styles.get_style_code('row') # Force generation of default row
867 self.styles.get_style_code('table') # Force generation of default table
868 self.styles.get_style_code('cell') # Force generation of default cell
869 self.manifest_files = [] # List of extra files included
870 self.manifest_index = 1 # Index of added manifest files
873 self.parser_element_list = []
874 self.parser_element = ""
875 self.parser_sheet_num = 0
876 self.parser_sheet_row = 0
877 self.parser_sheet_column = 0
878 self.parser_cell_repeats = 0
879 self.parser_cell_string_pending = False
880 self.parser_cell_string_line = ""
882 # See if we need to read a document
884 # Verify that the document exists
885 if self.debug: print "Opening Document: %s" % opendoc
887 # Okay, now we load the file
890 def debug_level(self, level):
892 True if you want debugging messages
897 def file_mimetype(self, filename):
898 "Determine the filetype from the filename"
899 parts = filename.lower().split('.')
901 if (ext == 'png'): return (ext, "image/png")
902 if (ext == 'gif'): return (ext, "image/gif")
903 return (ext, "image/unknown")
905 def add_file(self, filename):
906 """Prepare a file for loading into ooolib
908 The filename should be the local filesystem name for
909 the file. The file is then prepared to be included in
910 the creation of the final document. The file needs to
911 remain in place so that it is available when the actual
912 document creation happens.
914 # mimetype set to (ext, filetype)
915 mimetype = self.file_mimetype(filename)
916 newname = "Pictures/%08d.%s" % (self.manifest_index, mimetype[0])
917 self.manifest_index += 1
918 filetype = mimetype[1]
919 self.manifest_files.append((filename, filetype, newname))
922 def set_meta(self, metaname, value):
923 "Set meta data in your document."
924 self.meta.set_meta(metaname, value)
926 def get_meta_value(self, metaname):
927 "Get meta data value for a given metaname"
928 return self.meta.get_meta_value(metaname)
930 def get_sheet_name(self):
931 "Returns the sheet name"
932 return self.sheets[self.sheet_index].get_name()
934 def get_sheet_dimensions(self):
935 "Returns the sheet dimensions in (cols, rows)"
936 return self.sheets[self.sheet_index].get_sheet_dimensions()
938 def set_column_property(self, column, name, value):
939 "Set Column Properties"
941 # column number column needs column-width set to value
942 self.styles.set_property('column', 'style:column-width', value)
943 style_code = self.styles.get_style_code('column')
944 self.sheets[self.sheet_index].set_sheet_config(('col', column), style_code)
946 def set_row_property(self, row, name, value):
949 # row number row needs row-height set to value
950 self.styles.set_property('row', 'style:row-height', value)
951 style_code = self.styles.get_style_code('row')
952 self.sheets[self.sheet_index].set_sheet_config(('row', row), style_code)
954 def set_cell_property(self, name, value):
955 """Turn and off cell properties
957 Actual application of properties is handled by setting a value."""
958 # background images need to be handled a little differently
959 # because they need to also be inserted into the final document
960 if (name == 'backgroundimage'):
961 # Add file and modify value
962 value = self.add_file(value)
963 self.styles.set_property('cell', name, value)
965 def get_sheet_index(self):
966 "Return the current sheet index number"
967 return self.sheet_index
969 def set_sheet_index(self, index):
970 "Set the sheet index"
971 if type(index) == type(1):
972 if index >= 0 and index < len(self.sheets):
973 self.sheet_index = index
974 return self.sheet_index
976 def get_sheet_count(self):
977 "Returns the number of existing sheets"
978 return len(self.sheets)
980 def new_sheet(self, sheetname):
982 self.sheet_index = len(self.sheets)
983 self.sheets.append(CalcSheet(sheetname))
984 return self.sheet_index
986 def set_cell_value(self, col, row, datatype, value):
987 "Set the value for a given cell"
988 self.sheets[self.sheet_index].set_sheet_value((col, row), datatype, value)
989 style_code = self.styles.get_style_code('cell')
990 self.sheets[self.sheet_index].set_sheet_config((col, row), style_code)
992 def get_cell_value(self, col, row):
993 "Get a cell value tuple (type, value) for a given cell"
994 sheetvalue = self.sheets[self.sheet_index].get_sheet_value(col, row)
995 # We stop here if there is no value for sheetvalue
996 if sheetvalue == None: return sheetvalue
997 # Now check to see if we have a value tuple
998 if 'value' in sheetvalue:
999 return sheetvalue['value']
1003 def load(self, filename):
1004 """Load .ods spreadsheet.
1006 The load function loads data from a document into the current cells.
1008 # Read in the important files
1011 data = self._zip_read(filename, "meta.xml")
1012 self.meta.meta_parse(data)
1015 data = self._zip_read(filename, "content.xml")
1016 self.content_parse(data)
1018 # settings.xml - I do not remember putting anything here
1019 # styles.xml - I do not remember putting anything here
1021 def parse_content_start_element(self, name, attrs):
1022 if self.debug: print '* Start element:', name
1023 self.parser_element_list.append(name)
1024 self.parser_element = self.parser_element_list[-1]
1026 # Keep track of the current sheet number
1027 if (self.parser_element == 'table:table'):
1028 # Move to starting cell
1029 self.parser_sheet_row = 0
1030 self.parser_sheet_column = 0
1031 # Increment the sheet number count
1032 self.parser_sheet_num += 1
1033 if (self.parser_sheet_num - 1 != self.sheet_index):
1034 # We are not on the first sheet and need to create a new sheet.
1035 # We will automatically move to the new sheet
1036 sheetname = "Sheet%d" % self.parser_sheet_num
1037 if 'table:name' in attrs: sheetname = attrs['table:name']
1038 self.new_sheet(sheetname)
1040 # We are on the first sheet and will need to overwrite the default name
1041 sheetname = "Sheet%d" % self.parser_sheet_num
1042 if 'table:name' in attrs: sheetname = attrs['table:name']
1043 self.sheets[self.sheet_index].set_name(sheetname)
1045 # Update the row numbers
1046 if (self.parser_element == 'table:table-row'):
1047 self.parser_sheet_row += 1
1048 self.parser_sheet_column = 0
1050 # Okay, now keep track of the sheet cell data
1051 if (self.parser_element == 'table:table-cell'):
1052 # By default it will repeat zero times
1053 self.parser_cell_repeats = 0
1054 # We must be in a new column
1055 self.parser_sheet_column += 1
1056 # Set some default values
1059 # Get values from attrs hash
1060 if 'office:value-type' in attrs: datatype = attrs['office:value-type']
1061 if 'office:value' in attrs: value = attrs['office:value']
1062 if 'table:formula' in attrs:
1063 datatype = 'formula'
1064 value = attrs['table:formula']
1065 if datatype == 'string':
1067 self.parser_cell_string_pending = True
1068 self.parser_cell_string_line = ""
1069 if 'table:number-columns-repeated' in attrs:
1070 self.parser_cell_repeats = int(attrs['table:number-columns-repeated']) - 1
1071 # Set the cell value
1073 # I should do this once per cell repeat above 0
1074 for i in range(0, self.parser_cell_repeats+1):
1075 self.set_cell_value(self.parser_sheet_column+i, self.parser_sheet_row, datatype, value)
1077 # There are lots of interesting cases with table:table-cell data. One problem is
1078 # reading the number of embedded spaces correctly. This code should help us get
1079 # the number of spaces out.
1081 if (self.parser_element == 'text:s'):
1082 # This means we have a number of spaces
1084 if 'text:c' in attrs:
1085 count_alpha = attrs['text:c']
1086 if (count_alpha.isdigit()):
1087 count_num = int(count_alpha)
1088 # I am not sure what to do if we do not have a string pending
1089 if (self.parser_cell_string_pending == True):
1090 # Append the currect number of spaces to the end
1091 self.parser_cell_string_line = "%s%s" % (self.parser_cell_string_line, ' '*count_num)
1093 if (self.parser_element == 'text:tab-stop'):
1094 if (self.parser_cell_string_pending == True):
1095 self.parser_cell_string_line = "%s\t" % (self.parser_cell_string_line)
1097 if (self.parser_element == 'text:line-break'):
1098 if (self.parser_cell_string_pending == True):
1099 self.parser_cell_string_line = "%s\n" % (self.parser_cell_string_line)
1101 # Debugging statements
1102 if self.debug: print " List: ", self.parser_element_list
1103 if self.debug: print " Attributes: ", attrs
1106 def parse_content_end_element(self, name):
1107 if self.debug: print '* End element:', name
1108 if name != self.parser_element:
1109 print "Tag Mismatch: '%s' != '%s'" % (name, self.parser_element)
1110 self.parser_element_list.pop()
1112 # If the element was text:p and we are in string mode
1113 if (self.parser_element == 'text:p'):
1114 if (self.parser_cell_string_pending):
1115 self.parser_cell_string_pending = False
1117 # Take care of repeated cells
1118 if (self.parser_element == 'table:table-cell'):
1119 self.parser_sheet_column += self.parser_cell_repeats
1121 # Readjust parser_element_list and parser_element
1122 if (self.parser_element_list):
1123 self.parser_element = self.parser_element_list[-1]
1125 self.parser_element = ""
1127 def parse_content_char_data(self, data):
1128 if self.debug: print " Character data: ", repr(data)
1130 if (self.parser_element == 'text:p' or self.parser_element == 'text:span'):
1131 if (self.parser_cell_string_pending):
1132 # Set the string and leave string pending mode
1133 # This does feel a little kludgy, but it does the job
1134 self.parser_cell_string_line = "%s%s" % (self.parser_cell_string_line, data)
1136 # I should do this once per cell repeat above 0
1137 for i in range(0, self.parser_cell_repeats+1):
1138 self.set_cell_value(self.parser_sheet_column+i, self.parser_sheet_row,
1139 'string', self.parser_cell_string_line)
1142 def content_parse(self, data):
1143 "Parse Content Data from a content.xml file"
1145 # Debugging statements
1147 # Sometimes it helps to see the document that was read from
1152 parser = xml.parsers.expat.ParserCreate()
1153 # Set up parser callback functions
1154 parser.StartElementHandler = self.parse_content_start_element
1155 parser.EndElementHandler = self.parse_content_end_element
1156 parser.CharacterDataHandler = self.parse_content_char_data
1158 # Actually parse the data
1159 parser.Parse(data, 1)
1161 def save(self, filename):
1162 """Save .ods spreadsheet.
1164 The save function saves the current cells and settings into a document.
1166 if self.debug: print "Writing %s" % filename
1167 self.savefile = zipfile.ZipFile(filename, "w")
1168 if self.debug: print " meta.xml"
1169 self._zip_insert(self.savefile, "meta.xml", self.meta.get_meta())
1170 if self.debug: print " mimetype"
1171 self._zip_insert(self.savefile, "mimetype", "application/vnd.oasis.opendocument.spreadsheet")
1172 if self.debug: print " Configurations2/accelerator/current.xml"
1173 self._zip_insert(self.savefile, "Configurations2/accelerator/current.xml", "")
1174 if self.debug: print " META-INF/manifest.xml"
1175 self._zip_insert(self.savefile, "META-INF/manifest.xml", self._ods_manifest())
1176 if self.debug: print " content.xml"
1177 self._zip_insert(self.savefile, "content.xml", self._ods_content())
1178 if self.debug: print " settings.xml"
1179 self._zip_insert(self.savefile, "settings.xml", self._ods_settings())
1180 if self.debug: print " styles.xml"
1181 self._zip_insert(self.savefile, "styles.xml", self._ods_styles())
1183 # Add additional files if needed
1184 for fileset in self.manifest_files:
1185 (filename, filetype, newname) = fileset
1187 data = self._file_load(filename)
1188 if self.debug: print " Inserting '%s' as '%s'" % (filename, newname)
1189 self._zip_insert_binary(self.savefile, newname, data)
1191 def _file_load(self, filename):
1193 file = open(filename, "rb")
1198 def _zip_insert_binary(self, file, filename, data):
1199 "Insert a binary file into the zip archive"
1200 now = time.localtime(time.time())[:6]
1201 info = zipfile.ZipInfo(filename)
1202 info.date_time = now
1203 info.compress_type = zipfile.ZIP_DEFLATED
1204 file.writestr(info, data)
1207 def _zip_insert(self, file, filename, data):
1208 "Insert a file into the zip archive"
1210 # zip seems to struggle with non-ascii characters
1211 data = data.encode('utf-8')
1213 now = time.localtime(time.time())[:6]
1214 info = zipfile.ZipInfo(filename)
1215 info.date_time = now
1216 info.compress_type = zipfile.ZIP_DEFLATED
1217 file.writestr(info, data)
1219 def _zip_read(self, file, filename):
1220 "Get the data from a file in the zip archive by filename"
1221 file = zipfile.ZipFile(file, "r")
1222 data = file.read(filename)
1223 # Need to close the file
1227 def _ods_content(self):
1228 "Generate ods content.xml data"
1230 # This will list all of the sheets in the document
1231 self.sheetdata = ['tag', 'office:spreadsheet']
1232 for sheet in self.sheets:
1234 sheet_name = sheet.get_name()
1235 print " Creating Sheet '%s'" % sheet_name
1236 sheet_list = sheet.get_lists()
1237 self.sheetdata.append(sheet_list)
1239 self.automatic_styles = self.styles.get_automatic_styles()
1241 self.data = ['tag', 'office:document-content',
1242 ['element', 'xmlns:office', 'urn:oasis:names:tc:opendocument:xmlns:office:1.0'],
1243 ['element', 'xmlns:style', 'urn:oasis:names:tc:opendocument:xmlns:style:1.0'],
1244 ['element', 'xmlns:text', 'urn:oasis:names:tc:opendocument:xmlns:text:1.0'],
1245 ['element', 'xmlns:table', 'urn:oasis:names:tc:opendocument:xmlns:table:1.0'],
1246 ['element', 'xmlns:draw', 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0'],
1247 ['element', 'xmlns:fo', 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0'],
1248 ['element', 'xmlns:xlink', 'http://www.w3.org/1999/xlink'],
1249 ['element', 'xmlns:dc', 'http://purl.org/dc/elements/1.1/'],
1250 ['element', 'xmlns:meta', 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0'],
1251 ['element', 'xmlns:number', 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0'],
1252 ['element', 'xmlns:svg', 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0'],
1253 ['element', 'xmlns:chart', 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0'],
1254 ['element', 'xmlns:dr3d', 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0'],
1255 ['element', 'xmlns:math', 'http://www.w3.org/1998/Math/MathML'],
1256 ['element', 'xmlns:form', 'urn:oasis:names:tc:opendocument:xmlns:form:1.0'],
1257 ['element', 'xmlns:script', 'urn:oasis:names:tc:opendocument:xmlns:script:1.0'],
1258 ['element', 'xmlns:ooo', 'http://openoffice.org/2004/office'],
1259 ['element', 'xmlns:ooow', 'http://openoffice.org/2004/writer'],
1260 ['element', 'xmlns:oooc', 'http://openoffice.org/2004/calc'],
1261 ['element', 'xmlns:dom', 'http://www.w3.org/2001/xml-events'],
1262 ['element', 'xmlns:xforms', 'http://www.w3.org/2002/xforms'],
1263 ['element', 'xmlns:xsd', 'http://www.w3.org/2001/XMLSchema'],
1264 ['element', 'xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance'],
1265 ['element', 'office:version', '1.0'],
1266 ['tagline', 'office:scripts'],
1267 ['tag', 'office:font-face-decls',
1268 ['tagline', 'style:font-face',
1269 ['element', 'style:name', 'DejaVu Sans'],
1270 ['element', 'svg:font-family', ''DejaVu Sans''],
1271 ['element', 'style:font-pitch', 'variable']],
1272 ['tagline', 'style:font-face',
1273 ['element', 'style:name', 'Nimbus Sans L'],
1274 ['element', 'svg:font-family', ''Nimbus Sans L''],
1275 ['element', 'style:font-family-generic', 'swiss'],
1276 ['element', 'style:font-pitch', 'variable']]],
1279 self.automatic_styles,
1281 ['tag', 'office:body',
1282 self.sheetdata]] # Sheets are generated from the CalcSheet class
1284 # Generate content.xml XML data
1286 self.lines = xml.convert(self.data)
1287 self.filedata = '\n'.join(self.lines)
1288 # Return generated data
1289 return self.filedata
1291 def _ods_manifest(self):
1292 "Generate ods manifest.xml data"
1293 self.data = ['tag', 'manifest:manifest',
1294 ['element', 'xmlns:manifest', 'urn:oasis:names:tc:opendocument:xmlns:manifest:1.0'],
1295 ['tagline', 'manifest:file-entry',
1296 ['element', 'manifest:media-type', 'application/vnd.oasis.opendocument.spreadsheet'],
1297 ['element', 'manifest:full-path', '/']],
1298 ['tagline', 'manifest:file-entry',
1299 ['element', 'manifest:media-type', 'application/vnd.sun.xml.ui.configuration'],
1300 ['element', 'manifest:full-path', 'Configurations2/']],
1301 ['tagline', 'manifest:file-entry',
1302 ['element', 'manifest:media-type', ''],
1303 ['element', 'manifest:full-path', 'Configurations2/accelerator/']],
1304 ['tagline', 'manifest:file-entry',
1305 ['element', 'manifest:media-type', ''],
1306 ['element', 'manifest:full-path', 'Configurations2/accelerator/current.xml']],
1307 ['tagline', 'manifest:file-entry',
1308 ['element', 'manifest:media-type', 'text/xml'],
1309 ['element', 'manifest:full-path', 'content.xml']],
1310 ['tagline', 'manifest:file-entry',
1311 ['element', 'manifest:media-type', 'text/xml'],
1312 ['element', 'manifest:full-path', 'styles.xml']],
1313 ['tagline', 'manifest:file-entry',
1314 ['element', 'manifest:media-type', 'text/xml'],
1315 ['element', 'manifest:full-path', 'meta.xml']],
1316 ['tagline', 'manifest:file-entry',
1317 ['element', 'manifest:media-type', 'text/xml'],
1318 ['element', 'manifest:full-path', 'settings.xml']]]
1320 # Add additional files to manifest list
1321 for fileset in self.manifest_files:
1322 (filename, filetype, newname) = fileset
1323 addfile = ['tagline', 'manifest:file-entry',
1324 ['element', 'manifest:media-type', filetype],
1325 ['element', 'manifest:full-path', newname]]
1326 self.data.append(addfile)
1328 # Generate content.xml XML data
1330 self.lines = xml.convert(self.data)
1331 self.filedata = '\n'.join(self.lines)
1332 # Return generated data
1333 return self.filedata
1336 def _ods_settings(self):
1337 "Generate ods settings.xml data"
1338 self.data = ['tag', 'office:document-settings',
1339 ['element', 'xmlns:office', 'urn:oasis:names:tc:opendocument:xmlns:office:1.0'],
1340 ['element', 'xmlns:xlink', 'http://www.w3.org/1999/xlink'],
1341 ['element', 'xmlns:config', 'urn:oasis:names:tc:opendocument:xmlns:config:1.0'],
1342 ['element', 'xmlns:ooo', 'http://openoffice.org/2004/office'],
1343 ['element', 'office:version', '1.0'],
1344 ['tag', 'office:settings',
1345 ['tag', 'config:config-item-set',
1346 ['element', 'config:name', 'ooo:view-settings'],
1347 ['tag', 'config:config-item',
1348 ['element', 'config:name', 'VisibleAreaTop'],
1349 ['element', 'config:type', 'int'],
1351 ['tag', 'config:config-item',
1352 ['element', 'config:name', 'VisibleAreaLeft'],
1353 ['element', 'config:type', 'int'],
1355 ['tag', 'config:config-item',
1356 ['element', 'config:name', 'VisibleAreaWidth'],
1357 ['element', 'config:type', 'int'],
1359 ['tag', 'config:config-item',
1360 ['element', 'config:name', 'VisibleAreaHeight'],
1361 ['element', 'config:type', 'int'],
1363 ['tag', 'config:config-item-map-indexed',
1364 ['element', 'config:name', 'Views'],
1365 ['tag', 'config:config-item-map-entry',
1366 ['tag', 'config:config-item',
1367 ['element', 'config:name', 'ViewId'],
1368 ['element', 'config:type', 'string'],
1370 ['tag', 'config:config-item-map-named',
1371 ['element', 'config:name', 'Tables'],
1372 ['tag', 'config:config-item-map-entry',
1373 ['element', 'config:name', 'Sheet1'],
1374 ['tag', 'config:config-item',
1375 ['element', 'config:name', 'CursorPositionX'], # Cursor Position A
1376 ['element', 'config:type', 'int'],
1378 ['tag', 'config:config-item',
1379 ['element', 'config:name', 'CursorPositionY'], # Cursor Position 1
1380 ['element', 'config:type', 'int'],
1382 ['tag', 'config:config-item',
1383 ['element', 'config:name', 'HorizontalSplitMode'],
1384 ['element', 'config:type', 'short'],
1386 ['tag', 'config:config-item',
1387 ['element', 'config:name', 'VerticalSplitMode'],
1388 ['element', 'config:type', 'short'],
1390 ['tag', 'config:config-item',
1391 ['element', 'config:name', 'HorizontalSplitPosition'],
1392 ['element', 'config:type', 'int'],
1394 ['tag', 'config:config-item',
1395 ['element', 'config:name', 'VerticalSplitPosition'],
1396 ['element', 'config:type', 'int'],
1398 ['tag', 'config:config-item',
1399 ['element', 'config:name', 'ActiveSplitRange'],
1400 ['element', 'config:type', 'short'],
1402 ['tag', 'config:config-item',
1403 ['element', 'config:name', 'PositionLeft'],
1404 ['element', 'config:type', 'int'],
1406 ['tag', 'config:config-item',
1407 ['element', 'config:name', 'PositionRight'],
1408 ['element', 'config:type', 'int'],
1410 ['tag', 'config:config-item',
1411 ['element', 'config:name', 'PositionTop'],
1412 ['element', 'config:type', 'int'],
1414 ['tag', 'config:config-item',
1415 ['element', 'config:name', 'PositionBottom'],
1416 ['element', 'config:type', 'int'],
1418 ['tag', 'config:config-item',
1419 ['element', 'config:name', 'ActiveTable'],
1420 ['element', 'config:type', 'string'],
1421 ['data', 'Sheet1']],
1422 ['tag', 'config:config-item',
1423 ['element', 'config:name', 'HorizontalScrollbarWidth'],
1424 ['element', 'config:type', 'int'],
1426 ['tag', 'config:config-item',
1427 ['element', 'config:name', 'ZoomType'],
1428 ['element', 'config:type', 'short'],
1430 ['tag', 'config:config-item',
1431 ['element', 'config:name', 'ZoomValue'],
1432 ['element', 'config:type', 'int'],
1434 ['tag', 'config:config-item',
1435 ['element', 'config:name', 'PageViewZoomValue'],
1436 ['element', 'config:type', 'int'],
1438 ['tag', 'config:config-item',
1439 ['element', 'config:name', 'ShowPageBreakPreview'],
1440 ['element', 'config:type', 'boolean'],
1442 ['tag', 'config:config-item',
1443 ['element', 'config:name', 'ShowZeroValues'],
1444 ['element', 'config:type', 'boolean'],
1446 ['tag', 'config:config-item',
1447 ['element', 'config:name', 'ShowNotes'],
1448 ['element', 'config:type', 'boolean'],
1450 ['tag', 'config:config-item',
1451 ['element', 'config:name', 'ShowGrid'],
1452 ['element', 'config:type', 'boolean'],
1454 ['tag', 'config:config-item',
1455 ['element', 'config:name', 'GridColor'],
1456 ['element', 'config:type', 'long'],
1457 ['data', '12632256']],
1458 ['tag', 'config:config-item',
1459 ['element', 'config:name', 'ShowPageBreaks'],
1460 ['element', 'config:type', 'boolean'],
1462 ['tag', 'config:config-item',
1463 ['element', 'config:name', 'HasColumnRowHeaders'],
1464 ['element', 'config:type', 'boolean'],
1466 ['tag', 'config:config-item',
1467 ['element', 'config:name', 'HasSheetTabs'],
1468 ['element', 'config:type', 'boolean'],
1470 ['tag', 'config:config-item',
1471 ['element', 'config:name', 'IsOutlineSymbolsSet'],
1472 ['element', 'config:type', 'boolean'],
1474 ['tag', 'config:config-item',
1475 ['element', 'config:name', 'IsSnapToRaster'],
1476 ['element', 'config:type', 'boolean'],
1478 ['tag', 'config:config-item',
1479 ['element', 'config:name', 'RasterIsVisible'],
1480 ['element', 'config:type', 'boolean'],
1482 ['tag', 'config:config-item',
1483 ['element', 'config:name', 'RasterResolutionX'],
1484 ['element', 'config:type', 'int'],
1486 ['tag', 'config:config-item',
1487 ['element', 'config:name', 'RasterResolutionY'],
1488 ['element', 'config:type', 'int'],
1490 ['tag', 'config:config-item',
1491 ['element', 'config:name', 'RasterSubdivisionX'],
1492 ['element', 'config:type', 'int'],
1494 ['tag', 'config:config-item',
1495 ['element', 'config:name', 'RasterSubdivisionY'],
1496 ['element', 'config:type', 'int'],
1498 ['tag', 'config:config-item',
1499 ['element', 'config:name', 'IsRasterAxisSynchronized'],
1500 ['element', 'config:type', 'boolean'],
1501 ['data', 'true']]]]],
1502 ['tag', 'config:config-item-set',
1503 ['element', 'config:name', 'ooo:configuration-settings'],
1504 ['tag', 'config:config-item',
1505 ['element', 'config:name', 'ShowZeroValues'],
1506 ['element', 'config:type', 'boolean'],
1508 ['tag', 'config:config-item',
1509 ['element', 'config:name', 'ShowNotes'],
1510 ['element', 'config:type', 'boolean'],
1512 ['tag', 'config:config-item',
1513 ['element', 'config:name', 'ShowGrid'],
1514 ['element', 'config:type', 'boolean'],
1516 ['tag', 'config:config-item',
1517 ['element', 'config:name', 'GridColor'],
1518 ['element', 'config:type', 'long'],
1519 ['data', '12632256']],
1520 ['tag', 'config:config-item',
1521 ['element', 'config:name', 'ShowPageBreaks'],
1522 ['element', 'config:type', 'boolean'],
1524 ['tag', 'config:config-item',
1525 ['element', 'config:name', 'LinkUpdateMode'],
1526 ['element', 'config:type', 'short'],
1528 ['tag', 'config:config-item',
1529 ['element', 'config:name', 'HasColumnRowHeaders'],
1530 ['element', 'config:type', 'boolean'],
1532 ['tag', 'config:config-item',
1533 ['element', 'config:name', 'HasSheetTabs'],
1534 ['element', 'config:type', 'boolean'],
1536 ['tag', 'config:config-item',
1537 ['element', 'config:name', 'IsOutlineSymbolsSet'],
1538 ['element', 'config:type', 'boolean'],
1540 ['tag', 'config:config-item',
1541 ['element', 'config:name', 'IsSnapToRaster'],
1542 ['element', 'config:type', 'boolean'],
1544 ['tag', 'config:config-item',
1545 ['element', 'config:name', 'RasterIsVisible'],
1546 ['element', 'config:type', 'boolean'],
1548 ['tag', 'config:config-item',
1549 ['element', 'config:name', 'RasterResolutionX'],
1550 ['element', 'config:type', 'int'],
1552 ['tag', 'config:config-item',
1553 ['element', 'config:name', 'RasterResolutionY'],
1554 ['element', 'config:type', 'int'],
1556 ['tag', 'config:config-item',
1557 ['element', 'config:name', 'RasterSubdivisionX'],
1558 ['element', 'config:type', 'int'],
1560 ['tag', 'config:config-item',
1561 ['element', 'config:name', 'RasterSubdivisionY'],
1562 ['element', 'config:type', 'int'],
1564 ['tag', 'config:config-item',
1565 ['element', 'config:name', 'IsRasterAxisSynchronized'],
1566 ['element', 'config:type', 'boolean'],
1568 ['tag', 'config:config-item',
1569 ['element', 'config:name', 'AutoCalculate'],
1570 ['element', 'config:type', 'boolean'],
1572 ['tag', 'config:config-item',
1573 ['element', 'config:name', 'PrinterName'],
1574 ['element', 'config:type', 'string'],
1575 ['data', 'Generic Printer']],
1576 ['tag', 'config:config-item',
1577 ['element', 'config:name', 'PrinterSetup'],
1578 ['element', 'config:type', 'base64Binary'],
1579 ['data', 'YgH+/0dlbmVyaWMgUHJpbnRlcgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU0dFTlBSVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWAAMAqAAAAAAA//8FAFZUAAAkbQAASm9iRGF0YSAxCnByaW50ZXI9R2VuZXJpYyBQcmludGVyCm9yaWVudGF0aW9uPVBvcnRyYWl0CmNvcGllcz0xCnNjYWxlPTEwMAptYXJnaW5kYWp1c3RtZW50PTAsMCwwLDAKY29sb3JkZXB0aD0yNApwc2xldmVsPTAKY29sb3JkZXZpY2U9MApQUERDb250ZXhEYXRhClBhZ2VTaXplOkxldHRlcgAA']],
1580 ['tag', 'config:config-item',
1581 ['element', 'config:name', 'ApplyUserData'],
1582 ['element', 'config:type', 'boolean'],
1584 ['tag', 'config:config-item',
1585 ['element', 'config:name', 'CharacterCompressionType'],
1586 ['element', 'config:type', 'short'],
1588 ['tag', 'config:config-item',
1589 ['element', 'config:name', 'IsKernAsianPunctuation'],
1590 ['element', 'config:type', 'boolean'],
1592 ['tag', 'config:config-item',
1593 ['element', 'config:name', 'SaveVersionOnClose'],
1594 ['element', 'config:type', 'boolean'],
1596 ['tag', 'config:config-item',
1597 ['element', 'config:name', 'UpdateFromTemplate'],
1598 ['element', 'config:type', 'boolean'],
1600 ['tag', 'config:config-item',
1601 ['element', 'config:name', 'AllowPrintJobCancel'],
1602 ['element', 'config:type', 'boolean'],
1604 ['tag', 'config:config-item',
1605 ['element', 'config:name', 'LoadReadonly'],
1606 ['element', 'config:type', 'boolean'],
1607 ['data', 'false']]]]]
1609 # Generate content.xml XML data
1611 self.lines = xml.convert(self.data)
1612 self.filedata = '\n'.join(self.lines)
1613 # Return generated data
1614 return self.filedata
1617 def _ods_styles(self):
1618 "Generate ods styles.xml data"
1619 self.data = ['tag', 'office:document-styles',
1620 ['element', 'xmlns:office', 'urn:oasis:names:tc:opendocument:xmlns:office:1.0'],
1621 ['element', 'xmlns:style', 'urn:oasis:names:tc:opendocument:xmlns:style:1.0'],
1622 ['element', 'xmlns:text', 'urn:oasis:names:tc:opendocument:xmlns:text:1.0'],
1623 ['element', 'xmlns:table', 'urn:oasis:names:tc:opendocument:xmlns:table:1.0'],
1624 ['element', 'xmlns:draw', 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0'],
1625 ['element', 'xmlns:fo', 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0'],
1626 ['element', 'xmlns:xlink', 'http://www.w3.org/1999/xlink'],
1627 ['element', 'xmlns:dc', 'http://purl.org/dc/elements/1.1/'],
1628 ['element', 'xmlns:meta', 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0'],
1629 ['element', 'xmlns:number', 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0'],
1630 ['element', 'xmlns:svg', 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0'],
1631 ['element', 'xmlns:chart', 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0'],
1632 ['element', 'xmlns:dr3d', 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0'],
1633 ['element', 'xmlns:math', 'http://www.w3.org/1998/Math/MathML'],
1634 ['element', 'xmlns:form', 'urn:oasis:names:tc:opendocument:xmlns:form:1.0'],
1635 ['element', 'xmlns:script', 'urn:oasis:names:tc:opendocument:xmlns:script:1.0'],
1636 ['element', 'xmlns:ooo', 'http://openoffice.org/2004/office'],
1637 ['element', 'xmlns:ooow', 'http://openoffice.org/2004/writer'],
1638 ['element', 'xmlns:oooc', 'http://openoffice.org/2004/calc'],
1639 ['element', 'xmlns:dom', 'http://www.w3.org/2001/xml-events'],
1640 ['element', 'office:version', '1.0'],
1641 ['tag', 'office:font-face-decls',
1642 ['tagline', 'style:font-face',
1643 ['element', 'style:name', 'DejaVu Sans'],
1644 ['element', 'svg:font-family', ''DejaVu Sans''],
1645 ['element', 'style:font-pitch', 'variable']],
1646 ['tagline', 'style:font-face',
1647 ['element', 'style:name', 'Nimbus Sans L'],
1648 ['element', 'svg:font-family', ''Nimbus Sans L''],
1649 ['element', 'style:font-family-generic', 'swiss'],
1650 ['element', 'style:font-pitch', 'variable']]],
1651 ['tag', 'office:styles',
1652 ['tag', 'style:default-style',
1653 ['element', 'style:family', 'table-cell'],
1654 ['tagline', 'style:table-cell-properties',
1655 ['element', 'style:decimal-places', '2']],
1656 ['tagline', 'style:paragraph-properties',
1657 ['element', 'style:tab-stop-distance', '0.5in']],
1658 ['tagline', 'style:text-properties',
1659 ['element', 'style:font-name', 'Nimbus Sans L'],
1660 ['element', 'fo:language', 'en'],
1661 ['element', 'fo:country', 'US'],
1662 ['element', 'style:font-name-asian', 'DejaVu Sans'],
1663 ['element', 'style:language-asian', 'none'],
1664 ['element', 'style:country-asian', 'none'],
1665 ['element', 'style:font-name-complex', 'DejaVu Sans'],
1666 ['element', 'style:language-complex', 'none'],
1667 ['element', 'style:country-complex', 'none']]],
1668 ['tag', 'number:number-style',
1669 ['element', 'style:name', 'N0'],
1670 ['tagline', 'number:number',
1671 ['element', 'number:min-integer-digits', '1']]],
1672 ['tag', 'number:currency-style',
1673 ['element', 'style:name', 'N104P0'],
1674 ['element', 'style:volatile', 'true'],
1675 ['tag', 'number:currency-symbol',
1676 ['element', 'number:language', 'en'],
1677 ['element', 'number:country', 'US'],
1679 ['tagline', 'number:number',
1680 ['element', 'number:decimal-places', '2'],
1681 ['element', 'number:min-integer-digits', '1'],
1682 ['element', 'number:grouping', 'true']]],
1683 ['tag', 'number:currency-style',
1684 ['element', 'style:name', 'N104'],
1685 ['tagline', 'style:text-properties',
1686 ['element', 'fo:color', '#ff0000']],
1687 ['tag', 'number:text',
1689 ['tag', 'number:currency-symbol',
1690 ['element', 'number:language', 'en'],
1691 ['element', 'number:country', 'US'],
1693 ['tagline', 'number:number',
1694 ['element', 'number:decimal-places', '2'],
1695 ['element', 'number:min-integer-digits', '1'],
1696 ['element', 'number:grouping', 'true']],
1697 ['tagline', 'style:map',
1698 ['element', 'style:condition', 'value()>=0'],
1699 ['element', 'style:apply-style-name', 'N104P0']]],
1700 ['tagline', 'style:style',
1701 ['element', 'style:name', 'Default'],
1702 ['element', 'style:family', 'table-cell']],
1703 ['tag', 'style:style',
1704 ['element', 'style:name', 'Result'],
1705 ['element', 'style:family', 'table-cell'],
1706 ['element', 'style:parent-style-name', 'Default'],
1707 ['tagline', 'style:text-properties',
1708 ['element', 'fo:font-style', 'italic'],
1709 ['element', 'style:text-underline-style', 'solid'],
1710 ['element', 'style:text-underline-width', 'auto'],
1711 ['element', 'style:text-underline-color', 'font-color'],
1712 ['element', 'fo:font-weight', 'bold']]],
1713 ['tagline', 'style:style',
1714 ['element', 'style:name', 'Result2'],
1715 ['element', 'style:family', 'table-cell'],
1716 ['element', 'style:parent-style-name', 'Result'],
1717 ['element', 'style:data-style-name', 'N104']],
1718 ['tag', 'style:style',
1719 ['element', 'style:name', 'Heading'],
1720 ['element', 'style:family', 'table-cell'],
1721 ['element', 'style:parent-style-name', 'Default'],
1722 ['tagline', 'style:table-cell-properties',
1723 ['element', 'style:text-align-source', 'fix'],
1724 ['element', 'style:repeat-content', 'false']],
1725 ['tagline', 'style:paragraph-properties',
1726 ['element', 'fo:text-align', 'center']],
1727 ['tagline', 'style:text-properties',
1728 ['element', 'fo:font-size', '16pt'],
1729 ['element', 'fo:font-style', 'italic'],
1730 ['element', 'fo:font-weight', 'bold']]],
1731 ['tag', 'style:style',
1732 ['element', 'style:name', 'Heading1'],
1733 ['element', 'style:family', 'table-cell'],
1734 ['element', 'style:parent-style-name', 'Heading'],
1735 ['tagline', 'style:table-cell-properties',
1736 ['element', 'style:rotation-angle', '90']]]],
1737 ['tag', 'office:automatic-styles',
1738 ['tag', 'style:page-layout',
1739 ['element', 'style:name', 'pm1'],
1740 ['tagline', 'style:page-layout-properties',
1741 ['element', 'style:writing-mode', 'lr-tb']],
1742 ['tag', 'style:header-style',
1743 ['tagline', 'style:header-footer-properties',
1744 ['element', 'fo:min-height', '0.2957in'],
1745 ['element', 'fo:margin-left', '0in'],
1746 ['element', 'fo:margin-right', '0in'],
1747 ['element', 'fo:margin-bottom', '0.0984in']]],
1748 ['tag', 'style:footer-style',
1749 ['tagline', 'style:header-footer-properties',
1750 ['element', 'fo:min-height', '0.2957in'],
1751 ['element', 'fo:margin-left', '0in'],
1752 ['element', 'fo:margin-right', '0in'],
1753 ['element', 'fo:margin-top', '0.0984in']]]],
1754 ['tag', 'style:page-layout',
1755 ['element', 'style:name', 'pm2'],
1756 ['tagline', 'style:page-layout-properties',
1757 ['element', 'style:writing-mode', 'lr-tb']],
1758 ['tag', 'style:header-style',
1759 ['tag', 'style:header-footer-properties',
1760 ['element', 'fo:min-height', '0.2957in'],
1761 ['element', 'fo:margin-left', '0in'],
1762 ['element', 'fo:margin-right', '0in'],
1763 ['element', 'fo:margin-bottom', '0.0984in'],
1764 ['element', 'fo:border', '0.0346in solid #000000'],
1765 ['element', 'fo:padding', '0.0071in'],
1766 ['element', 'fo:background-color', '#c0c0c0'],
1767 ['tagline', 'style:background-image']]],
1768 ['tag', 'style:footer-style',
1769 ['tag', 'style:header-footer-properties',
1770 ['element', 'fo:min-height', '0.2957in'],
1771 ['element', 'fo:margin-left', '0in'],
1772 ['element', 'fo:margin-right', '0in'],
1773 ['element', 'fo:margin-top', '0.0984in'],
1774 ['element', 'fo:border', '0.0346in solid #000000'],
1775 ['element', 'fo:padding', '0.0071in'],
1776 ['element', 'fo:background-color', '#c0c0c0'],
1777 ['tagline', 'style:background-image']]]]],
1778 ['tag', 'office:master-styles',
1779 ['tag', 'style:master-page',
1780 ['element', 'style:name', 'Default'],
1781 ['element', 'style:page-layout-name', 'pm1'],
1782 ['tag', 'style:header',
1784 ['data', '<text:sheet-name>???</text:sheet-name>']]],
1785 ['tagline', 'style:header-left',
1786 ['element', 'style:display', 'false']],
1787 ['tag', 'style:footer',
1789 ['data', 'Page <text:page-number>1</text:page-number>']]],
1790 ['tagline', 'style:footer-left',
1791 ['element', 'style:display', 'false']]],
1792 ['tag', 'style:master-page',
1793 ['element', 'style:name', 'Report'],
1794 ['element', 'style:page-layout-name', 'pm2'],
1795 ['tag', 'style:header',
1796 ['tag', 'style:region-left',
1798 ['data', '<text:sheet-name>???</text:sheet-name> (<text:title>???</text:title>)']]],
1799 ['tag', 'style:region-right',
1801 ['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>']]]],
1802 ['tagline', 'style:header-left',
1803 ['element', 'style:display', 'false']],
1804 ['tag', 'style:footer',
1806 ['data', 'Page <text:page-number>1</text:page-number> / <text:page-count>99</text:page-count>']]],
1807 ['tagline', 'style:footer-left',
1808 ['element', 'style:display', 'false']]]]]
1811 # Generate content.xml XML data
1813 self.lines = xml.convert(self.data)
1814 self.filedata = '\n'.join(self.lines)
1815 # Return generated data
1816 return self.filedata
1819 "Writer Class - Used to create OpenDocument Format Writer Documents."
1821 "Initialize ooolib Writer instance"
1822 # Default to no debugging
1824 self.meta = Meta('odt')
1826 def set_meta(self, metaname, value):
1827 "Set meta data in your document."
1828 self.meta.set_meta(metaname, value)
1830 def save(self, filename):
1831 """Save .odt document
1833 The save function saves the current .odt document.
1835 if self.debug: print "Writing %s" % filename
1836 self.savefile = zipfile.ZipFile(filename, "w")
1837 if self.debug: print " meta.xml"
1838 self._zip_insert(self.savefile, "meta.xml", self.meta.get_meta())
1839 if self.debug: print " mimetype"
1840 self._zip_insert(self.savefile, "mimetype", "application/vnd.oasis.opendocument.text")
1841 if self.debug: print " META-INF/manifest.xml"
1842 self._zip_insert(self.savefile, "META-INF/manifest.xml", self._odt_manifest())
1843 if self.debug: print " content.xml"
1844 self._zip_insert(self.savefile, "content.xml", self._odt_content())
1845 if self.debug: print " settings.xml"
1846 # self._zip_insert(self.savefile, "settings.xml", self._odt_settings())
1847 if self.debug: print " styles.xml"
1848 # self._zip_insert(self.savefile, "styles.xml", self._odt_styles())
1850 # We need to close the file now that we are done creating it.
1851 self.savefile.close()
1853 def _zip_insert(self, file, filename, data):
1854 now = time.localtime(time.time())[:6]
1855 info = zipfile.ZipInfo(filename)
1856 info.date_time = now
1857 info.compress_type = zipfile.ZIP_DEFLATED
1858 file.writestr(info, data)
1860 def _odt_manifest(self):
1861 "Generate odt manifest.xml data"
1863 self.data = ['tag', 'manifest:manifest',
1864 ['element', 'xmlns:manifest', 'urn:oasis:names:tc:opendocument:xmlns:manifest:1.0'],
1865 ['tagline', 'manifest:file-entry',
1866 ['element', 'manifest:media-type', 'application/vnd.oasis.opendocument.text'],
1867 ['element', 'manifest:full-path', '/']],
1868 ['tagline', 'manifest:file-entry',
1869 ['element', 'manifest:media-type', 'text/xml'],
1870 ['element', 'manifest:full-path', 'content.xml']],
1871 ['tagline', 'manifest:file-entry',
1872 ['element', 'manifest:media-type', 'text/xml'],
1873 ['element', 'manifest:full-path', 'styles.xml']],
1874 ['tagline', 'manifest:file-entry',
1875 ['element', 'manifest:media-type', 'text/xml'],
1876 ['element', 'manifest:full-path', 'meta.xml']],
1877 ['tagline', 'manifest:file-entry',
1878 ['element', 'manifest:media-type', 'text/xml'],
1879 ['element', 'manifest:full-path', 'settings.xml']]]
1881 # Generate content.xml XML data
1883 self.lines = xml.convert(self.data)
1884 self.lines.insert(1, '<!DOCTYPE manifest:manifest PUBLIC "-//OpenOffice.org//DTD Manifest 1.0//EN" "Manifest.dtd">')
1885 self.filedata = '\n'.join(self.lines)
1886 # Return generated data
1887 return self.filedata
1889 def _odt_content(self):
1890 "Generate odt content.xml data"
1892 self.data = ['tag', 'office:document-content',
1893 ['element', 'xmlns:office', 'urn:oasis:names:tc:opendocument:xmlns:office:1.0'],
1894 ['element', 'xmlns:style', 'urn:oasis:names:tc:opendocument:xmlns:style:1.0'],
1895 ['element', 'xmlns:text', 'urn:oasis:names:tc:opendocument:xmlns:text:1.0'],
1896 ['element', 'xmlns:table', 'urn:oasis:names:tc:opendocument:xmlns:table:1.0'],
1897 ['element', 'xmlns:draw', 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0'],
1898 ['element', 'xmlns:fo', 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0'],
1899 ['element', 'xmlns:xlink', 'http://www.w3.org/1999/xlink'],
1900 ['element', 'xmlns:dc', 'http://purl.org/dc/elements/1.1/'],
1901 ['element', 'xmlns:meta', 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0'],
1902 ['element', 'xmlns:number', 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0'],
1903 ['element', 'xmlns:svg', 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0'],
1904 ['element', 'xmlns:chart', 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0'],
1905 ['element', 'xmlns:dr3d', 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0'],
1906 ['element', 'xmlns:math', 'http://www.w3.org/1998/Math/MathML'],
1907 ['element', 'xmlns:form', 'urn:oasis:names:tc:opendocument:xmlns:form:1.0'],
1908 ['element', 'xmlns:script', 'urn:oasis:names:tc:opendocument:xmlns:script:1.0'],
1909 ['element', 'xmlns:ooo', 'http://openoffice.org/2004/office'],
1910 ['element', 'xmlns:ooow', 'http://openoffice.org/2004/writer'],
1911 ['element', 'xmlns:oooc', 'http://openoffice.org/2004/calc'],
1912 ['element', 'xmlns:dom', 'http://www.w3.org/2001/xml-events'],
1913 ['element', 'xmlns:xforms', 'http://www.w3.org/2002/xforms'],
1914 ['element', 'xmlns:xsd', 'http://www.w3.org/2001/XMLSchema'],
1915 ['element', 'xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance'],
1916 ['element', 'office:version', '1.0'],
1917 ['tagline', 'office:scripts'],
1918 ['tag', 'office:font-face-decls',
1919 ['tagline', 'style:font-face',
1920 ['element', 'style:name', 'DejaVu Sans'],
1921 ['element', 'svg:font-family', ''DejaVu Sans''],
1922 ['element', 'style:font-pitch', 'variable']],
1923 ['tagline', 'style:font-face',
1924 ['element', 'style:name', 'Nimbus Roman No9 L'],
1925 ['element', 'svg:font-family', ''Nimbus Roman No9 L''],
1926 ['element', 'style:font-family-generic', 'roman'],
1927 ['element', 'style:font-pitch', 'variable']],
1928 ['tagline', 'style:font-face',
1929 ['element', 'style:name', 'Nimbus Sans L'],
1930 ['element', 'svg:font-family', ''Nimbus Sans L''],
1931 ['element', 'style:font-family-generic', 'swiss'],
1932 ['element', 'style:font-pitch', 'variable']]],
1933 ['tagline', 'office:automatic-styles'],
1934 ['tag', 'office:body',
1935 ['tag', 'office:text',
1936 ['tagline', 'office:forms',
1937 ['element', 'form:automatic-focus', 'false'],
1938 ['element', 'form:apply-design-mode', 'false']],
1939 ['tag', 'text:sequence-decls',
1940 ['tagline', 'text:sequence-decl',
1941 ['element', 'text:display-outline-level', '0'],
1942 ['element', 'text:name', 'Illustration']],
1943 ['tagline', 'text:sequence-decl',
1944 ['element', 'text:display-outline-level', '0'],
1945 ['element', 'text:name', 'Table']],
1946 ['tagline', 'text:sequence-decl',
1947 ['element', 'text:display-outline-level', '0'],
1948 ['element', 'text:name', 'Text']],
1949 ['tagline', 'text:sequence-decl',
1950 ['element', 'text:display-outline-level', '0'],
1951 ['element', 'text:name', 'Drawing']]],
1952 ['tagline', 'text:p',
1953 ['element', 'text:style-name', 'Standard']]]]]
1955 # Generate content.xml XML data
1957 self.lines = xml.convert(self.data)
1958 self.filedata = '\n'.join(self.lines)
1959 # Return generated data
1960 return self.filedata