1 # -*- coding: utf-8 -*-
2 #modification pour python 3 : Laurent Mérat, 6x7 - mai 2020
4 "ooolib-python - Copyright (C) 2006-2009 Joseph Colton"
6 # PEUT-ETRE une MISE A JOUR ???
8 # ooolib-python - Python module for creating Open Document Format documents.
9 # Copyright (C) 2006-2009 Joseph Colton
11 # This library is free software; you can redistribute it and/or
12 # modify it under the terms of the GNU Lesser General Public
13 # License as published by the Free Software Foundation; either
14 # version 2.1 of the License, or (at your option) any later version.
16 # This library is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 # Lesser General Public License for more details.
21 # You should have received a copy of the GNU Lesser General Public
22 # License along with this library; if not, write to the Free Software
23 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
25 # You can contact me by email at josephcolton@gmail.com
27 # Import Standard Modules
28 import zipfile # Needed for reading/writing documents
34 import xml.parsers.expat # Needed for parsing documents
37 "Get the ooolib-python version number"
41 "Get the ooolib-python version"
42 return "ooolib-python-%s" % version_number()
44 def clean_string(data):
45 "Returns an XML friendly copy of the data string"
46 data = str(data) # This line thanks to Chris Ender
47 data = data.replace('&', '&')
48 data = data.replace("'", ''')
49 data = data.replace('"', '"')
50 data = data.replace('<', '<')
51 data = data.replace('>', '>')
52 data = data.replace('\t', '<text:tab-stop/>')
53 data = data.replace('\n', '<text:line-break/>')
58 "XML Class - Used to convert nested lists into XML"
61 "Initialize ooolib XML instance"
64 def _xmldata(self, data):
65 datatype = data.pop(0)
66 datavalue = data.pop(0)
67 outstring = '%s' % datavalue
70 def _xmltag(self, data):
73 datatype = data.pop(0)
74 dataname = data.pop(0)
75 outstring = '<%s' % dataname
81 if (newdata[0] == 'element' and element):
82 newstring = self._xmlelement(newdata)
83 outstring = '%s %s' % (outstring, newstring)
85 if (newdata[0] != 'element' and element):
87 outstring = '%s>' % outstring
88 if (newdata[0] == 'tag' or newdata[0] == 'tagline'):
89 outstring = '%s\n' % outstring
90 if (newdata[0] == 'tag'):
91 newstring = self._xmltag(newdata)
92 outstring = '%s%s' % (outstring, newstring)
94 if (newdata[0] == 'tagline'):
95 newstring = self._xmltagline(newdata)
96 outstring = '%s%s' % (outstring, newstring)
98 if (newdata[0] == 'data'):
99 newstring = self._xmldata(newdata)
100 outstring = '%s%s' % (outstring, newstring)
104 outstring = '%s>\n' % outstring
105 outstring = '%s</%s>\n' % (outstring, dataname)
108 def _xmltagline(self, data):
111 datatype = data.pop(0)
112 dataname = data.pop(0)
113 outstring = '<%s' % dataname
117 newdata = data.pop(0)
118 if (newdata[0] != 'element'): break
119 newstring = self._xmlelement(newdata)
120 outstring = '%s %s' % (outstring, newstring)
121 outstring = '%s/>\n' % outstring
122 # Non-Element Section should not exist
125 def _xmlelement(self, data):
126 datatype = data.pop(0)
127 dataname = data.pop(0)
128 datavalue = data.pop(0)
129 outstring = '%s="%s"' % (dataname, datavalue)
132 def convert(self, data):
133 """Convert nested lists into XML
135 The convert method takes a nested lists and converts them
136 into XML to be used in Open Document Format documents.
137 There are three types of lists that are recognized at this
138 time. They are as follows:
140 'tag' - Tag opens a set of data that is eventually closed
145 'tagline' - Taglines are similar to tags, except they open
146 and close themselves.
147 List: ['tagline', 'xml']
150 'element' - Elements are pieces of information stored in an
151 opening tag or tagline.
152 List: ['element', 'color', 'blue']
155 'data' - Data is plain text directly inserted into the XML
157 List: ['data', 'hello']
160 Bring them all together for something like this.
163 ['tag', 'xml', ['element', 'a', 'b'], ['tagline', 'xml2'],
167 <xml a="b"><xml2/>asdf</xml>
170 outlines.append('<?xml version="1.0" encoding="UTF-8"?>')
171 if (type(data) == type([]) and len(data) > 0):
172 if data[0] == 'tag': outlines.append(self._xmltag(data))
179 def __init__(self, doctype, debug=False):
180 self.doctype = doctype
183 # The generator should always default to the version number
184 self.meta_generator = version()
186 self.meta_subject = ''
187 self.meta_description = ''
188 self.meta_keywords = []
189 self.meta_creator = 'ooolib-python'
190 self.meta_editor = ''
191 self.meta_user1_name = 'Info 1'
192 self.meta_user2_name = 'Info 2'
193 self.meta_user3_name = 'Info 3'
194 self.meta_user4_name = 'Info 4'
195 self.meta_user1_value = ''
196 self.meta_user2_value = ''
197 self.meta_user3_value = ''
198 self.meta_user4_value = ''
199 self.meta_creation_date = self.meta_time()
201 self.parser_element_list = []
202 self.parser_element = ""
203 self.parser_count = 0
205 def set_meta(self, metaname, value):
206 """Set meta data in your document.
208 Currently implemented metaname options are as follows:
209 'creator' - The document author
211 if metaname == 'creator': self.meta_creator = value
212 if metaname == 'editor': self.meta_editor = value
213 if metaname == 'title': self.meta_title = value
214 if metaname == 'subject': self.meta_subject = value
215 if metaname == 'description': self.meta_description = value
216 if metaname == 'user1name': self.meta_user1_name = value
217 if metaname == 'user2name': self.meta_user2_name = value
218 if metaname == 'user3name': self.meta_user3_name = value
219 if metaname == 'user4name': self.meta_user4_name = value
220 if metaname == 'user1value': self.meta_user1_value = value
221 if metaname == 'user2value': self.meta_user2_value = value
222 if metaname == 'user3value': self.meta_user3_value = value
223 if metaname == 'user4value': self.meta_user4_value = value
224 if metaname == 'keyword':
225 if value not in self.meta_keywords:
226 self.meta_keywords.append(value)
228 def get_meta_value(self, metaname):
229 "Get meta data value for a given metaname."
230 if metaname == 'creator': return self.meta_creator
231 if metaname == 'editor': return self.meta_editor
232 if metaname == 'title': return self.meta_title
233 if metaname == 'subject': return self.meta_subject
234 if metaname == 'description': return self.meta_description
235 if metaname == 'user1name': return self.meta_user1_name
236 if metaname == 'user2name': return self.meta_user2_name
237 if metaname == 'user3name': return self.meta_user3_name
238 if metaname == 'user4name': return self.meta_user4_name
239 if metaname == 'user1value': return self.meta_user1_value
240 if metaname == 'user2value': return self.meta_user2_value
241 if metaname == 'user3value': return self.meta_user3_value
242 if metaname == 'user4value': return self.meta_user4_value
243 if metaname == 'keyword': return self.meta_keywords
246 "Return time string in meta data format"
248 stamp = "%04d-%02d-%02dT%02d:%02d:%02d" % (t[0], t[1], t[2], t[3], t[4], t[5])
251 def parse_start_element(self, name, attrs):
252 if self.debug: print('* Start element:', name)
253 self.parser_element_list.append(name)
254 self.parser_element = self.parser_element_list[-1]
255 # Need the meta name from the user-defined tags
256 if (self.parser_element == "meta:user-defined"):
257 self.parser_count += 1
258 # Set user-defined name
259 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)
264 def parse_end_element(self, name):
265 if self.debug: print('* End element:', name)
266 if name != self.parser_element:
267 print("Tag Mismatch: '%s' != '%s'" % (name, self.parser_element))
268 self.parser_element_list.pop()
269 # Readjust parser_element_list and parser_element
270 if (self.parser_element_list):
271 self.parser_element = self.parser_element_list[-1]
273 self.parser_element = ""
275 def parse_char_data(self, data):
277 print(" Character data: ", repr(data))
278 # Collect Meta data fields
279 if (self.parser_element == "dc:title"):
280 self.set_meta("title", data)
281 if (self.parser_element == "dc:description"):
282 self.set_meta("description", data)
283 if (self.parser_element == "dc:subject"):
284 self.set_meta("subject", data)
285 if (self.parser_element == "meta:initial-creator"):
286 self.set_meta("creator", data)
287 # Try to maintain the same creation date
288 if (self.parser_element == "meta:creation-date"):
289 self.meta_creation_date = data
290 # The user defined fields need to be kept track of, parser_count does that
291 if (self.parser_element == "meta:user-defined"):
292 self.set_meta("user%dvalue" % self.parser_count, data)
294 def meta_parse(self, data):
295 "Parse Meta Data from a meta.xml file"
296 # Debugging statements
298 # Sometimes it helps to see the document that was read from
302 parser = xml.parsers.expat.ParserCreate()
303 # Set up parser callback functions
304 parser.StartElementHandler = self.parse_start_element
305 parser.EndElementHandler = self.parse_end_element
306 parser.CharacterDataHandler = self.parse_char_data
307 # Actually parse the data
308 parser.Parse(data, 1)
311 "Generate meta.xml file data"
312 self.meta_date = self.meta_time()
313 self.data = ['tag', 'office:document-meta',
314 ['element', 'xmlns:office', 'urn:oasis:names:tc:opendocument:xmlns:office:1.0'],
315 ['element', 'xmlns:xlink', 'http://www.w3.org/1999/xlink'],
316 ['element', 'xmlns:dc', 'http://purl.org/dc/elements/1.1/'],
317 ['element', 'xmlns:meta', 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0'],
318 ['element', 'xmlns:ooo', 'http://openoffice.org/2004/office'],
319 ['element', 'office:version', '1.0'],
320 ['tag', 'office:meta',
321 ['tag', 'meta:generator', # Was: 'OpenOffice.org/2.0$Linux OpenOffice.org_project/680m5$Build-9011'
322 ['data', self.meta_generator]], # Generator is set the the ooolib-python version.
324 ['data', self.meta_title]], # This data is the document title
325 ['tag', 'dc:description',
326 ['data', self.meta_description]], # This data is the document description
327 ['tag', 'dc:subject',
328 ['data', self.meta_subject]], # This data is the document subject
329 ['tag', 'meta:initial-creator',
330 ['data', self.meta_creator]], # This data is the document creator
331 ['tag', 'meta:creation-date',
332 ['data', self.meta_creation_date]], # This is the original creation date of the document
333 ['tag', 'dc:creator',
334 ['data', self.meta_editor]], # This data is the document editor
336 ['data', self.meta_date]], # This is the last modified date of the document
337 ['tag', 'dc:language',
338 ['data', 'en-US']], # We will probably always use en-US for language
339 ['tag', 'meta:editing-cycles',
340 ['data', '1']], # Edit cycles will probably always be 1 for generated documents
341 ['tag', 'meta:editing-duration',
342 ['data', 'PT0S']], # Editing duration is modified - creation date
343 ['tag', 'meta:user-defined',
344 ['element', 'meta:name', self.meta_user1_name],
345 ['data', self.meta_user1_value]],
346 ['tag', 'meta:user-defined',
347 ['element', 'meta:name', self.meta_user2_name],
348 ['data', self.meta_user2_value]],
349 ['tag', 'meta:user-defined',
350 ['element', 'meta:name', self.meta_user3_name],
351 ['data', self.meta_user3_value]],
352 ['tag', 'meta:user-defined',
353 ['element', 'meta:name', self.meta_user4_name],
354 ['data', self.meta_user4_value]]]]
355 # ['tagline', 'meta:document-statistic',
356 # ['element', 'meta:table-count', len(self.sheets)], # len(self.sheets) ?
357 # ['element', 'meta:cell-count', '15']]]] # Not sure how to keep track
358 # Generate content.xml XML data
360 self.lines = xml.convert(self.data)
361 self.filedata = '\n'.join(self.lines)
362 # Return generated data
367 "Calc Style Management - Used to keep track of created styles."
370 self.style_config = {}
373 self.style_column = 1
376 # Style Properties (Defaults) - To be used later
377 self.property_column_width_default = '0.8925in' # Default Column Width
378 self.property_row_height_default = '0.189in' # Default Row Height
380 self.property_column_width = '0.8925in' # Default Column Width
381 self.property_row_height = '0.189in' # Default Row Height
382 self.property_cell_bold = False # Bold off be default
383 self.property_cell_italic = False # Italic off be default
384 self.property_cell_underline = False # Underline off be default
385 self.property_cell_fg_color = 'default' # Text Color Default
386 self.property_cell_bg_color = 'default' # Cell Background Default
387 self.property_cell_bg_image = 'none' # Cell Background Default
388 self.property_cell_fontsize = '10' # Cell Font Size Default
389 self.property_cell_valign = 'default' # Vertial Alignment Default
390 self.property_cell_halign = 'default' # Horizantal Alignment Default
392 def get_next_style(self, style):
393 "Returns the next style code for the given style"
396 style_code = 'ta%d' % self.style_table
398 if style == 'column':
399 style_code = 'co%d' % self.style_column
402 style_code = 'ro%d' % self.style_row
405 style_code = 'ce%d' % self.style_cell
409 def set_property(self, style, name, value):
410 "Sets a property which will later be turned into a code"
413 if style == 'column':
414 if name == 'style:column-width': self.property_column_width = value
416 if name == 'style:row-height': self.property_row_height = value
418 if name == 'bold' and type(value) == type(True): self.property_cell_bold = value
419 if name == 'italic' and type(value) == type(True): self.property_cell_italic = value
420 if name == 'underline' and type(value) == type(True): self.property_cell_underline = value
421 if name == 'fontsize': self.property_cell_fontsize = value
423 self.property_cell_fg_color = 'default'
424 redata = re.search("^(#[\da-fA-F]{6})$", value)
425 if redata: self.property_cell_fg_color = value.lower()
426 if name == 'background':
427 self.property_cell_bg_color = 'default'
428 redata = re.search("^(#[\da-fA-F]{6})$", value)
429 if redata: self.property_cell_bg_color = value.lower()
430 if name == 'backgroundimage':
431 self.property_cell_bg_image = value
433 self.property_cell_valign = value
435 self.property_cell_halign = value
437 def get_style_code(self, style):
441 if style == 'column':
442 style_data = tuple([style,
443 ('style:column-width', self.property_column_width)])
444 if style_data in self.style_config:
445 # Style Exists, return code
446 style_code = self.style_config[style_data]
448 # Style does not exist, create code and return it
449 style_code = self.get_next_style(style)
450 self.style_config[style_data] = style_code
452 style_data = tuple([style,
453 ('style:row-height', self.property_row_height)])
454 if style_data in self.style_config:
455 # Style Exists, return code
456 style_code = self.style_config[style_data]
458 # Style does not exist, create code and return it
459 style_code = self.get_next_style(style)
460 self.style_config[style_data] = style_code
463 # Add additional styles
464 if self.property_cell_bold: style_data.append(('bold', True))
465 if self.property_cell_italic: style_data.append(('italic', True))
466 if self.property_cell_underline: style_data.append(('underline', True))
467 if self.property_cell_fontsize != '10':
468 style_data.append(('fontsize', self.property_cell_fontsize))
469 if self.property_cell_fg_color != 'default':
470 style_data.append(('color', self.property_cell_fg_color))
471 if self.property_cell_bg_color != 'default':
472 style_data.append(('background', self.property_cell_bg_color))
473 if self.property_cell_bg_image != 'none':
474 style_data.append(('backgroundimage', self.property_cell_bg_image))
475 if self.property_cell_valign != 'default':
476 style_data.append(('valign', self.property_cell_valign))
477 if self.property_cell_halign != 'default':
478 style_data.append(('halign', self.property_cell_halign))
479 style_data = tuple(style_data)
480 if style_data in self.style_config:
481 # Style Exists, return code
482 style_code = self.style_config[style_data]
484 # Style does not exist, create code and return it
485 style_code = self.get_next_style(style)
486 self.style_config[style_data] = style_code
489 def get_automatic_styles(self):
490 "Return 'office:automatic-styles' lists"
491 automatic_styles = ['tag', 'office:automatic-styles']
492 for style_data in self.style_config:
493 style_code = self.style_config[style_data]
494 style_data = list(style_data)
495 style = style_data.pop(0)
496 if style == 'column':
497 style_list = ['tag', 'style:style',
498 ['element', 'style:name', style_code], # Column 'co1' properties
499 ['element', 'style:family', 'table-column']]
500 tagline = ['tagline', 'style:table-column-properties',
501 ['element', 'fo:break-before', 'auto']] # unsure what break before means
502 for set in style_data:
504 if name == 'style:column-width':
505 tagline.append(['element', 'style:column-width', value])
506 style_list.append(tagline)
507 automatic_styles.append(style_list)
509 style_list = ['tag', 'style:style',
510 ['element', 'style:name', style_code], # Column 'ro1' properties
511 ['element', 'style:family', 'table-row']]
512 tagline = ['tagline', 'style:table-row-properties']
513 for set in style_data:
515 if name == 'style:row-height':
516 tagline.append(['element', 'style:row-height', value])
517 tagline.append(['element', 'fo:break-before', 'auto'])
518 # tagline.append(['element', 'style:use-optimal-row-height', 'true']) # Overrides settings
519 style_list.append(tagline)
520 automatic_styles.append(style_list)
522 style_list = ['tag', 'style:style',
523 ['element', 'style:name', style_code], # ce1 style
524 ['element', 'style:family', 'table-cell'], # cell
525 ['element', 'style:parent-style-name', 'Default']] # parent is Default
527 tagline = ['tag', 'style:table-cell-properties']
528 tagline_additional = []
529 for set in style_data:
531 if name == 'background':
532 tagline.append(['element', 'fo:background-color', value])
533 if name == 'backgroundimage':
534 tagline.append(['element', 'fo:background-color', 'transparent'])
535 # Additional tags added later
536 bgimagetag = ['tagline', 'style:background-image']
537 bgimagetag.append(['element', 'xlink:href', value])
538 bgimagetag.append(['element', 'xlink:type', 'simple'])
539 bgimagetag.append(['element', 'xlink:actuate', 'onLoad'])
540 tagline_additional.append(bgimagetag)
542 if value in ['top', 'bottom', 'middle']:
543 tagline.append(['element', 'style:vertical-align', value])
545 tagline.append(['element', 'style:text-align-source', 'fix'])
546 if value in ['filled']:
547 tagline.append(['element', 'style:repeat-content', 'true'])
549 tagline.append(['element', 'style:repeat-content', 'false'])
550 # Add any additional internal tags
551 while tagline_additional:
552 tagadd = tagline_additional.pop(0)
553 tagline.append(tagadd)
554 style_list.append(tagline)
555 # Paragraph Properties
556 tagline = ['tagline', 'style:paragraph-properties']
557 tagline_valid = False
558 for set in style_data:
562 if value in ['center']:
563 tagline.append(['element', 'fo:text-align', 'center'])
564 if value in ['end', 'right']:
565 tagline.append(['element', 'fo:text-align', 'end'])
566 if value in ['start', 'filled', 'left']:
567 tagline.append(['element', 'fo:text-align', 'start'])
568 if value in ['justify']:
569 tagline.append(['element', 'fo:text-align', 'justify'])
570 # Conditionally add the tagline
572 style_list.append(tagline)
574 tagline = ['tagline', 'style:text-properties']
575 for set in style_data:
578 tagline.append(['element', 'fo:font-weight', 'bold'])
580 tagline.append(['element', 'fo:font-style', 'italic'])
581 if name == 'underline':
582 tagline.append(['element', 'style:text-underline-style', 'solid'])
583 tagline.append(['element', 'style:text-underline-width', 'auto'])
584 tagline.append(['element', 'style:text-underline-color', 'font-color'])
586 tagline.append(['element', 'fo:color', value])
587 if name == 'fontsize':
588 tagline.append(['element', 'fo:font-size', '%spt' % value])
589 style_list.append(tagline)
590 automatic_styles.append(style_list)
592 automatic_styles.append(['tag', 'style:style',
593 ['element', 'style:name', 'ta1'],
594 ['element', 'style:family', 'table'],
595 ['element', 'style:master-page-name', 'Default'],
596 ['tagline', 'style:table-properties',
597 ['element', 'table:display', 'true'],
598 ['element', 'style:writing-mode', 'lr-tb']]])
599 return automatic_styles
603 "Calc Sheet Class - Used to keep track of the data for an individual sheet."
605 def __init__(self, sheetname):
607 self.sheet_name = sheetname
608 self.sheet_values = {}
609 self.sheet_config = {}
613 def get_sheet_dimensions(self):
614 "Returns the max column and row"
615 return (self.max_col, self.max_row)
617 def clean_formula(self, data):
618 "Returns a formula for use in ODF"
619 # Example Translations
621 # datavalue = 'oooc:=SUM([.A1:.A2])'
622 # '=IF((A5>A4);A4;"")'
623 # datavalue = 'oooc:=IF(([.A5]>[.A4]);[.A4];"")'
625 data = clean_string(data)
626 redata = re.search('^=([A-Z]+)(\(.*)$', data)
628 # funct is the function name. The rest if the string will be the functArgs
629 funct = redata.group(1)
630 functArgs = redata.group(2)
631 # Search for cell lebels and replace them
632 reList = re.findall('([A-Z]+\d+)', functArgs)
633 # sort and keep track so we do not do a cell more than once
637 # Replace each cell label
638 curVar = reList.pop()
639 if curVar == lastVar: continue
641 functArgs = functArgs.replace(curVar, '[.%s]' % curVar)
642 data = 'oooc:=%s%s' % (funct, functArgs)
646 "Returns the sheet name"
647 return self.sheet_name
649 def set_name(self, sheetname):
650 "Resets the sheet name"
651 self.sheet_name = sheetname
653 def get_sheet_values(self):
654 "Returns the sheet cell values"
655 return self.sheet_values
657 def get_sheet_value(self, col, row):
658 "Get the value contents of a cell"
660 if cell in self.sheet_values:
661 return self.sheet_values[cell]
665 def get_sheet_config(self):
666 "Returns the sheet cell properties"
667 return self.sheet_config
669 def set_sheet_config(self, location, style_code):
670 "Sets Style Code for a given location"
671 self.sheet_config[location] = style_code
673 def set_sheet_value(self, cell, datatype, datavalue):
674 """Sets the value for a specific cell
676 cell must be in the format (col, row) where row and col are int.
677 Example: B5 would be written as (2, 5)
678 datatype must be one of 'string', 'float', 'formula'
679 datavalue should be a string
682 if type(cell) != type(()) or len(cell) != 2:
683 print("Invalid Cell")
686 if type(col) != type(1):
687 print("Invalid Cell")
689 if type(row) != type(1):
690 print("Invalid Cell")
693 if datatype in ['string', 'annotation']:
694 datavalue = clean_string(datavalue)
695 # Fix Link Data. Link's value is a tuple containing (url, description)
696 if (datatype == 'link'):
697 url = clean_string(datavalue[0])
698 desc = clean_string(datavalue[1])
699 datavalue = (url, desc)
701 if datatype == 'formula':
702 datavalue = self.clean_formula(datavalue)
703 # Adjust maximum sizes
704 if col > self.max_col: self.max_col = col
705 if row > self.max_row: self.max_row = row
706 datatype = str(datatype)
707 if (datatype not in ['string', 'float', 'formula', 'annotation', 'link']):
708 # Set all unknown cell types to string
710 datavalue = str(datavalue)
711 # The following lines are taken directly from HPS
712 # self.sheet_values[cell] = (datatype, datavalue)
713 # HPS: Cell content is now a list of tuples instead of a tuple
714 # While storing here, store the cell contents first and the annotation next. While generating the XML reverse this
715 contents = self.sheet_values.get(cell, {'annotation':None,'link':None, 'value':None})
716 if datatype == 'annotation':
717 contents['annotation'] = (datatype, datavalue)
718 elif datatype == 'link':
719 contents['link'] = (datatype, datavalue)
721 contents['value'] = (datatype, datavalue)
722 self.sheet_values[cell] = contents
725 "Returns nested lists for XML processing"
726 if (self.max_col == 0 and self.max_row == 0):
727 sheet_lists = ['tag', 'table:table',
728 ['element', 'table:name', self.sheet_name], # Set the Sheet Name
729 ['element', 'table:style-name', 'ta1'],
730 ['element', 'table:print', 'false'],
731 ['tagline', 'table:table-column',
732 ['element', 'table:style-name', 'co1'],
733 ['element', 'table:default-cell-style-name', 'Default']],
734 ['tag', 'table:table-row',
735 ['element', 'table:style-name', 'ro1'],
736 ['tagline', 'table:table-cell']]]
739 sheet_lists = ['tag', 'table:table',
740 ['element', 'table:name', self.sheet_name], # Set the sheet name
741 ['element', 'table:style-name', 'ta1'],
742 ['element', 'table:print', 'false']]
743 # ['tagline', 'table:table-column',
744 # ['element', 'table:style-name', 'co1'],
745 # ['element', 'table:number-columns-repeated', self.max_col], # max_col? '2'
746 # ['element', 'table:default-cell-style-name', 'Default']],
747 # Need to add column information
748 for col in range(1, self.max_col+1):
749 location = ('col', col)
751 if location in self.sheet_config:
752 style_code = self.sheet_config[location]
753 sheet_lists.append(['tagline', 'table:table-column',
754 ['element', 'table:style-name', style_code],
755 ['element', 'table:default-cell-style-name', 'Default']])
756 # Need to create each row
757 for row in range(1, self.max_row + 1):
758 location = ('row', row)
760 if location in self.sheet_config:
761 style_code = self.sheet_config[location]
762 rowlist = ['tag', 'table:table-row',
763 ['element', 'table:style-name', style_code]]
764 for col in range(1, self.max_col + 1):
766 style_code = 'ce1' # Default all cells to ce1
767 if cell in self.sheet_config:
768 style_code = self.sheet_config[cell] # Lookup cell if available
769 if cell in self.sheet_values:
770 # (datatype, datavalue) = self.sheet_values[cell] # Marked for removal
771 collist = ['tag', 'table:table-cell']
772 if style_code != 'ce1':
773 collist.append(['element', 'table:style-name', style_code])
774 # Contents, annotations, and links added by HPS
775 contents = self.sheet_values[cell] # cell contents is a dictionary
776 if contents['value']:
777 (datatype, datavalue) = contents['value']
778 if datatype == 'float':
779 collist.append(['element', 'office:value-type', datatype])
780 collist.append(['element', 'office:value', datavalue])
781 if datatype == 'string':
782 collist.append(['element', 'office:value-type', datatype])
783 if datatype == 'formula':
784 collist.append(['element', 'table:formula', datavalue])
785 collist.append(['element', 'office:value-type', 'float'])
786 collist.append(['element', 'office:value', '0'])
790 if contents['annotation']:
791 (annotype, annoval) = contents['annotation']
792 collist.append(['tag', 'office:annotation',
793 ['tag', 'text:p', ['data', annoval]]])
795 (linktype, linkval) = contents['link']
797 collist.append(['tag', 'text:p', ['data', datavalue],
798 ['tag', 'text:a', ['element', 'xlink:href', linkval[0]],
799 ['data', linkval[1]]]])
800 else: # no value; just fill the link
801 collist.append(['tag', 'text:p',
802 ['tag', 'text:a', ['element', 'xlink:href', linkval[0]],
803 ['data', linkval[1]]]])
806 collist.append(['tag', 'text:p', ['data', datavalue]])
808 collist = ['tagline', 'table:table-cell']
809 rowlist.append(collist)
810 sheet_lists.append(rowlist)
815 "Calc Class - Used to create OpenDocument Format Calc Spreadsheets."
817 def __init__(self, sheetname=None, opendoc=None, debug=False):
818 "Initialize ooolib Calc instance"
819 # Default to no debugging
821 if not sheetname: sheetname = "Sheet1"
822 self.sheets = [CalcSheet(sheetname)] # The main sheet will be initially called 'Sheet1'
823 self.sheet_index = 0 # We initially start on the first sheet
824 self.styles = CalcStyles()
825 self.meta = Meta('ods')
826 self.styles.get_style_code('column') # Force generation of default column
827 self.styles.get_style_code('row') # Force generation of default row
828 self.styles.get_style_code('table') # Force generation of default table
829 self.styles.get_style_code('cell') # Force generation of default cell
830 self.manifest_files = [] # List of extra files included
831 self.manifest_index = 1 # Index of added manifest files
833 self.parser_element_list = []
834 self.parser_element = ""
835 self.parser_sheet_num = 0
836 self.parser_sheet_row = 0
837 self.parser_sheet_column = 0
838 self.parser_cell_repeats = 0
839 self.parser_cell_string_pending = False
840 self.parser_cell_string_line = ""
841 # See if we need to read a document
843 # Verify that the document exists
844 if self.debug: print("Opening Document: %s" % opendoc)
845 # Okay, now we load the file
848 def debug_level(self, level):
850 True if you want debugging messages
855 def file_mimetype(self, filename):
856 "Determine the filetype from the filename"
857 parts = filename.lower().split('.')
859 if (ext == 'png'): return (ext, "image/png")
860 if (ext == 'gif'): return (ext, "image/gif")
861 return (ext, "image/unknown")
863 def add_file(self, filename):
864 """Prepare a file for loading into ooolib
866 The filename should be the local filesystem name for
867 the file. The file is then prepared to be included in
868 the creation of the final document. The file needs to
869 remain in place so that it is available when the actual
870 document creation happens.
872 # mimetype set to (ext, filetype)
873 mimetype = self.file_mimetype(filename)
874 newname = "Pictures/%08d.%s" % (self.manifest_index, mimetype[0])
875 self.manifest_index += 1
876 filetype = mimetype[1]
877 self.manifest_files.append((filename, filetype, newname))
880 def set_meta(self, metaname, value):
881 "Set meta data in your document."
882 self.meta.set_meta(metaname, value)
884 def get_meta_value(self, metaname):
885 "Get meta data value for a given metaname"
886 return self.meta.get_meta_value(metaname)
888 def get_sheet_name(self):
889 "Returns the sheet name"
890 return self.sheets[self.sheet_index].get_name()
892 def get_sheet_dimensions(self):
893 "Returns the sheet dimensions in (cols, rows)"
894 return self.sheets[self.sheet_index].get_sheet_dimensions()
896 def set_column_property(self, column, name, value):
897 "Set Column Properties"
899 # column number column needs column-width set to value
900 self.styles.set_property('column', 'style:column-width', value)
901 style_code = self.styles.get_style_code('column')
902 self.sheets[self.sheet_index].set_sheet_config(('col', column), style_code)
904 def set_row_property(self, row, name, value):
907 # row number row needs row-height set to value
908 self.styles.set_property('row', 'style:row-height', value)
909 style_code = self.styles.get_style_code('row')
910 self.sheets[self.sheet_index].set_sheet_config(('row', row), style_code)
912 def set_cell_property(self, name, value):
913 """Turn and off cell properties
915 Actual application of properties is handled by setting a value."""
916 # background images need to be handled a little differently
917 # because they need to also be inserted into the final document
918 if (name == 'backgroundimage'):
919 # Add file and modify value
920 value = self.add_file(value)
921 self.styles.set_property('cell', name, value)
923 def get_sheet_index(self):
924 "Return the current sheet index number"
925 return self.sheet_index
927 def set_sheet_index(self, index):
928 "Set the sheet index"
929 if type(index) == type(1):
930 if index >= 0 and index < len(self.sheets):
931 self.sheet_index = index
932 return self.sheet_index
934 def get_sheet_count(self):
935 "Returns the number of existing sheets"
936 return len(self.sheets)
938 def new_sheet(self, sheetname):
940 self.sheet_index = len(self.sheets)
941 self.sheets.append(CalcSheet(sheetname))
942 return self.sheet_index
944 def set_cell_value(self, col, row, datatype, value):
945 "Set the value for a given cell"
946 self.sheets[self.sheet_index].set_sheet_value((col, row), datatype, value)
947 style_code = self.styles.get_style_code('cell')
948 self.sheets[self.sheet_index].set_sheet_config((col, row), style_code)
950 def get_cell_value(self, col, row):
951 "Get a cell value tuple (type, value) for a given cell"
952 sheetvalue = self.sheets[self.sheet_index].get_sheet_value(col, row)
953 # We stop here if there is no value for sheetvalue
954 if sheetvalue == None: return sheetvalue
955 # Now check to see if we have a value tuple
956 if 'value' in sheetvalue:
957 return sheetvalue['value']
961 def load(self, filename):
962 """Load .ods spreadsheet.
964 The load function loads data from a document into the current cells.
966 # Read in the important files
968 data = self._zip_read(filename, "meta.xml")
969 self.meta.meta_parse(data)
971 data = self._zip_read(filename, "content.xml")
972 self.content_parse(data)
973 # settings.xml - I do not remember putting anything here
974 # styles.xml - I do not remember putting anything here
976 def parse_content_start_element(self, name, attrs):
977 if self.debug: print('* Start element:', name)
978 self.parser_element_list.append(name)
979 self.parser_element = self.parser_element_list[-1]
980 # Keep track of the current sheet number
981 if (self.parser_element == 'table:table'):
982 # Move to starting cell
983 self.parser_sheet_row = 0
984 self.parser_sheet_column = 0
985 # Increment the sheet number count
986 self.parser_sheet_num += 1
987 if (self.parser_sheet_num - 1 != self.sheet_index):
988 # We are not on the first sheet and need to create a new sheet.
989 # We will automatically move to the new sheet
990 sheetname = "Sheet%d" % self.parser_sheet_num
991 if 'table:name' in attrs: sheetname = attrs['table:name']
992 self.new_sheet(sheetname)
994 # We are on the first sheet and will need to overwrite the default name
995 sheetname = "Sheet%d" % self.parser_sheet_num
996 if 'table:name' in attrs: sheetname = attrs['table:name']
997 self.sheets[self.sheet_index].set_name(sheetname)
998 # Update the row numbers
999 if (self.parser_element == 'table:table-row'):
1000 self.parser_sheet_row += 1
1001 self.parser_sheet_column = 0
1002 # Okay, now keep track of the sheet cell data
1003 if (self.parser_element == 'table:table-cell'):
1004 # By default it will repeat zero times
1005 self.parser_cell_repeats = 0
1006 # We must be in a new column
1007 self.parser_sheet_column += 1
1008 # Set some default values
1011 # Get values from attrs hash
1012 if 'office:value-type' in attrs: datatype = attrs['office:value-type']
1013 if 'office:value' in attrs: value = attrs['office:value']
1014 if 'table:formula' in attrs:
1015 datatype = 'formula'
1016 value = attrs['table:formula']
1017 if datatype == 'string':
1019 self.parser_cell_string_pending = True
1020 self.parser_cell_string_line = ""
1021 if 'table:number-columns-repeated' in attrs:
1022 self.parser_cell_repeats = int(attrs['table:number-columns-repeated']) - 1
1023 # Set the cell value
1025 # I should do this once per cell repeat above 0
1026 for i in range(0, self.parser_cell_repeats+1):
1027 self.set_cell_value(self.parser_sheet_column+i, self.parser_sheet_row, datatype, value)
1028 # There are lots of interesting cases with table:table-cell data. One problem is
1029 # reading the number of embedded spaces correctly. This code should help us get
1030 # the number of spaces out.
1031 if (self.parser_element == 'text:s'):
1032 # This means we have a number of spaces
1034 if 'text:c' in attrs:
1035 count_alpha = attrs['text:c']
1036 if (count_alpha.isdigit()):
1037 count_num = int(count_alpha)
1038 # I am not sure what to do if we do not have a string pending
1039 if (self.parser_cell_string_pending == True):
1040 # Append the currect number of spaces to the end
1041 self.parser_cell_string_line = "%s%s" % (self.parser_cell_string_line, ' '*count_num)
1042 if (self.parser_element == 'text:tab-stop'):
1043 if (self.parser_cell_string_pending == True):
1044 self.parser_cell_string_line = "%s\t" % (self.parser_cell_string_line)
1045 if (self.parser_element == 'text:line-break'):
1046 if (self.parser_cell_string_pending == True):
1047 self.parser_cell_string_line = "%s\n" % (self.parser_cell_string_line)
1048 # Debugging statements
1049 if self.debug: print(" List: ", self.parser_element_list)
1050 if self.debug: print(" Attributes: ", attrs)
1052 def parse_content_end_element(self, name):
1053 if self.debug: print('* End element:', name)
1054 if name != self.parser_element:
1055 print("Tag Mismatch: '%s' != '%s'" % (name, self.parser_element))
1056 self.parser_element_list.pop()
1057 # If the element was text:p and we are in string mode
1058 if (self.parser_element == 'text:p'):
1059 if (self.parser_cell_string_pending):
1060 self.parser_cell_string_pending = False
1061 # Take care of repeated cells
1062 if (self.parser_element == 'table:table-cell'):
1063 self.parser_sheet_column += self.parser_cell_repeats
1064 # Readjust parser_element_list and parser_element
1065 if (self.parser_element_list):
1066 self.parser_element = self.parser_element_list[-1]
1068 self.parser_element = ""
1070 def parse_content_char_data(self, data):
1072 print(" Character data: ", repr(data))
1073 if (self.parser_element == 'text:p' or self.parser_element == 'text:span'):
1074 if (self.parser_cell_string_pending):
1075 # Set the string and leave string pending mode
1076 # This does feel a little kludgy, but it does the job
1077 self.parser_cell_string_line = "%s%s" % (self.parser_cell_string_line, data)
1078 # I should do this once per cell repeat above 0
1079 for i in range(0, self.parser_cell_repeats+1):
1080 self.set_cell_value(self.parser_sheet_column+i, self.parser_sheet_row,
1081 'string', self.parser_cell_string_line)
1083 def content_parse(self, data):
1084 "Parse Content Data from a content.xml file"
1085 # Debugging statements
1087 # Sometimes it helps to see the document that was read from
1091 parser = xml.parsers.expat.ParserCreate()
1092 # Set up parser callback functions
1093 parser.StartElementHandler = self.parse_content_start_element
1094 parser.EndElementHandler = self.parse_content_end_element
1095 parser.CharacterDataHandler = self.parse_content_char_data
1096 # Actually parse the data
1097 parser.Parse(data, 1)
1099 def save(self, filename):
1100 """Save .ods spreadsheet.
1102 The save function saves the current cells and settings into a document.
1104 if self.debug: print("Writing %s" % filename)
1105 self.savefile = zipfile.ZipFile(filename, "w")
1106 if self.debug: print(" meta.xml")
1107 self._zip_insert(self.savefile, "meta.xml", self.meta.get_meta())
1108 if self.debug: print(" mimetype")
1109 self._zip_insert(self.savefile, "mimetype", "application/vnd.oasis.opendocument.spreadsheet")
1110 if self.debug: print(" Configurations2/accelerator/current.xml")
1111 self._zip_insert(self.savefile, "Configurations2/accelerator/current.xml", "")
1112 if self.debug: print(" META-INF/manifest.xml")
1113 self._zip_insert(self.savefile, "META-INF/manifest.xml", self._ods_manifest())
1114 if self.debug: print(" content.xml")
1115 self._zip_insert(self.savefile, "content.xml", self._ods_content())
1116 if self.debug: print(" settings.xml")
1117 self._zip_insert(self.savefile, "settings.xml", self._ods_settings())
1118 if self.debug: print(" styles.xml")
1119 self._zip_insert(self.savefile, "styles.xml", self._ods_styles())
1120 # Add additional files if needed
1121 for fileset in self.manifest_files:
1122 (filename, filetype, newname) = fileset
1124 data = self._file_load(filename)
1125 if self.debug: print(" Inserting '%s' as '%s'" % (filename, newname))
1126 self._zip_insert_binary(self.savefile, newname, data)
1128 def _file_load(self, filename):
1130 file = open(filename, "r")
1135 def _zip_insert_binary(self, file, filename, data):
1136 "Insert a binary file into the zip archive"
1137 now = time.localtime(time.time())[:6]
1138 info = zipfile.ZipInfo(filename)
1139 info.date_time = now
1140 info.compress_type = zipfile.ZIP_DEFLATED
1141 file.writestr(info, data)
1143 def _zip_insert(self, file, filename, data):
1144 "Insert a file into the zip archive"
1145 # zip seems to struggle with non-ascii characters
1146 data = data.encode('utf-8')
1147 now = time.localtime(time.time())[:6]
1148 info = zipfile.ZipInfo(filename)
1149 info.date_time = now
1150 info.compress_type = zipfile.ZIP_DEFLATED
1151 file.writestr(info, data)
1153 def _zip_read(self, file, filename):
1154 "Get the data from a file in the zip archive by filename"
1155 file = zipfile.ZipFile(file, "r")
1156 data = file.read(filename)
1157 # Need to close the file
1161 def _ods_content(self):
1162 "Generate ods content.xml data"
1163 # This will list all of the sheets in the document
1164 self.sheetdata = ['tag', 'office:spreadsheet']
1165 for sheet in self.sheets:
1167 sheet_name = sheet.get_name()
1168 print(" Creating Sheet '%s'" % sheet_name)
1169 sheet_list = sheet.get_lists()
1170 self.sheetdata.append(sheet_list)
1172 self.automatic_styles = self.styles.get_automatic_styles()
1173 self.data = ['tag', 'office:document-content',
1174 ['element', 'xmlns:office', 'urn:oasis:names:tc:opendocument:xmlns:office:1.0'],
1175 ['element', 'xmlns:style', 'urn:oasis:names:tc:opendocument:xmlns:style:1.0'],
1176 ['element', 'xmlns:text', 'urn:oasis:names:tc:opendocument:xmlns:text:1.0'],
1177 ['element', 'xmlns:table', 'urn:oasis:names:tc:opendocument:xmlns:table:1.0'],
1178 ['element', 'xmlns:draw', 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0'],
1179 ['element', 'xmlns:fo', 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0'],
1180 ['element', 'xmlns:xlink', 'http://www.w3.org/1999/xlink'],
1181 ['element', 'xmlns:dc', 'http://purl.org/dc/elements/1.1/'],
1182 ['element', 'xmlns:meta', 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0'],
1183 ['element', 'xmlns:number', 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0'],
1184 ['element', 'xmlns:svg', 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0'],
1185 ['element', 'xmlns:chart', 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0'],
1186 ['element', 'xmlns:dr3d', 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0'],
1187 ['element', 'xmlns:math', 'http://www.w3.org/1998/Math/MathML'],
1188 ['element', 'xmlns:form', 'urn:oasis:names:tc:opendocument:xmlns:form:1.0'],
1189 ['element', 'xmlns:script', 'urn:oasis:names:tc:opendocument:xmlns:script:1.0'],
1190 ['element', 'xmlns:ooo', 'http://openoffice.org/2004/office'],
1191 ['element', 'xmlns:ooow', 'http://openoffice.org/2004/writer'],
1192 ['element', 'xmlns:oooc', 'http://openoffice.org/2004/calc'],
1193 ['element', 'xmlns:dom', 'http://www.w3.org/2001/xml-events'],
1194 ['element', 'xmlns:xforms', 'http://www.w3.org/2002/xforms'],
1195 ['element', 'xmlns:xsd', 'http://www.w3.org/2001/XMLSchema'],
1196 ['element', 'xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance'],
1197 ['element', 'office:version', '1.0'],
1198 ['tagline', 'office:scripts'],
1199 ['tag', 'office:font-face-decls',
1200 ['tagline', 'style:font-face',
1201 ['element', 'style:name', 'DejaVu Sans'],
1202 ['element', 'svg:font-family', ''DejaVu Sans''],
1203 ['element', 'style:font-pitch', 'variable']],
1204 ['tagline', 'style:font-face',
1205 ['element', 'style:name', 'Nimbus Sans L'],
1206 ['element', 'svg:font-family', ''Nimbus Sans L''],
1207 ['element', 'style:font-family-generic', 'swiss'],
1208 ['element', 'style:font-pitch', 'variable']]],
1210 self.automatic_styles,
1211 ['tag', 'office:body',
1212 self.sheetdata]] # Sheets are generated from the CalcSheet class
1213 # Generate content.xml XML data
1215 self.lines = xml.convert(self.data)
1216 self.filedata = '\n'.join(self.lines)
1217 # Return generated data
1218 return self.filedata
1220 def _ods_manifest(self):
1221 "Generate ods manifest.xml data"
1222 self.data = ['tag', 'manifest:manifest',
1223 ['element', 'xmlns:manifest', 'urn:oasis:names:tc:opendocument:xmlns:manifest:1.0'],
1224 ['tagline', 'manifest:file-entry',
1225 ['element', 'manifest:media-type', 'application/vnd.oasis.opendocument.spreadsheet'],
1226 ['element', 'manifest:full-path', '/']],
1227 ['tagline', 'manifest:file-entry',
1228 ['element', 'manifest:media-type', 'application/vnd.sun.xml.ui.configuration'],
1229 ['element', 'manifest:full-path', 'Configurations2/']],
1230 ['tagline', 'manifest:file-entry',
1231 ['element', 'manifest:media-type', ''],
1232 ['element', 'manifest:full-path', 'Configurations2/accelerator/']],
1233 ['tagline', 'manifest:file-entry',
1234 ['element', 'manifest:media-type', ''],
1235 ['element', 'manifest:full-path', 'Configurations2/accelerator/current.xml']],
1236 ['tagline', 'manifest:file-entry',
1237 ['element', 'manifest:media-type', 'text/xml'],
1238 ['element', 'manifest:full-path', 'content.xml']],
1239 ['tagline', 'manifest:file-entry',
1240 ['element', 'manifest:media-type', 'text/xml'],
1241 ['element', 'manifest:full-path', 'styles.xml']],
1242 ['tagline', 'manifest:file-entry',
1243 ['element', 'manifest:media-type', 'text/xml'],
1244 ['element', 'manifest:full-path', 'meta.xml']],
1245 ['tagline', 'manifest:file-entry',
1246 ['element', 'manifest:media-type', 'text/xml'],
1247 ['element', 'manifest:full-path', 'settings.xml']]]
1248 # Add additional files to manifest list
1249 for fileset in self.manifest_files:
1250 (filename, filetype, newname) = fileset
1251 addfile = ['tagline', 'manifest:file-entry',
1252 ['element', 'manifest:media-type', filetype],
1253 ['element', 'manifest:full-path', newname]]
1254 self.data.append(addfile)
1255 # Generate content.xml XML data
1257 self.lines = xml.convert(self.data)
1258 self.filedata = '\n'.join(self.lines)
1259 # Return generated data
1260 return self.filedata
1262 def _ods_settings(self):
1263 "Generate ods settings.xml data"
1264 self.data = ['tag', 'office:document-settings',
1265 ['element', 'xmlns:office', 'urn:oasis:names:tc:opendocument:xmlns:office:1.0'],
1266 ['element', 'xmlns:xlink', 'http://www.w3.org/1999/xlink'],
1267 ['element', 'xmlns:config', 'urn:oasis:names:tc:opendocument:xmlns:config:1.0'],
1268 ['element', 'xmlns:ooo', 'http://openoffice.org/2004/office'],
1269 ['element', 'office:version', '1.0'],
1270 ['tag', 'office:settings',
1271 ['tag', 'config:config-item-set',
1272 ['element', 'config:name', 'ooo:view-settings'],
1273 ['tag', 'config:config-item',
1274 ['element', 'config:name', 'VisibleAreaTop'],
1275 ['element', 'config:type', 'int'],
1277 ['tag', 'config:config-item',
1278 ['element', 'config:name', 'VisibleAreaLeft'],
1279 ['element', 'config:type', 'int'],
1281 ['tag', 'config:config-item',
1282 ['element', 'config:name', 'VisibleAreaWidth'],
1283 ['element', 'config:type', 'int'],
1285 ['tag', 'config:config-item',
1286 ['element', 'config:name', 'VisibleAreaHeight'],
1287 ['element', 'config:type', 'int'],
1289 ['tag', 'config:config-item-map-indexed',
1290 ['element', 'config:name', 'Views'],
1291 ['tag', 'config:config-item-map-entry',
1292 ['tag', 'config:config-item',
1293 ['element', 'config:name', 'ViewId'],
1294 ['element', 'config:type', 'string'],
1296 ['tag', 'config:config-item-map-named',
1297 ['element', 'config:name', 'Tables'],
1298 ['tag', 'config:config-item-map-entry',
1299 ['element', 'config:name', 'Sheet1'],
1300 ['tag', 'config:config-item',
1301 ['element', 'config:name', 'CursorPositionX'], # Cursor Position A
1302 ['element', 'config:type', 'int'],
1304 ['tag', 'config:config-item',
1305 ['element', 'config:name', 'CursorPositionY'], # Cursor Position 1
1306 ['element', 'config:type', 'int'],
1308 ['tag', 'config:config-item',
1309 ['element', 'config:name', 'HorizontalSplitMode'],
1310 ['element', 'config:type', 'short'],
1312 ['tag', 'config:config-item',
1313 ['element', 'config:name', 'VerticalSplitMode'],
1314 ['element', 'config:type', 'short'],
1316 ['tag', 'config:config-item',
1317 ['element', 'config:name', 'HorizontalSplitPosition'],
1318 ['element', 'config:type', 'int'],
1320 ['tag', 'config:config-item',
1321 ['element', 'config:name', 'VerticalSplitPosition'],
1322 ['element', 'config:type', 'int'],
1324 ['tag', 'config:config-item',
1325 ['element', 'config:name', 'ActiveSplitRange'],
1326 ['element', 'config:type', 'short'],
1328 ['tag', 'config:config-item',
1329 ['element', 'config:name', 'PositionLeft'],
1330 ['element', 'config:type', 'int'],
1332 ['tag', 'config:config-item',
1333 ['element', 'config:name', 'PositionRight'],
1334 ['element', 'config:type', 'int'],
1336 ['tag', 'config:config-item',
1337 ['element', 'config:name', 'PositionTop'],
1338 ['element', 'config:type', 'int'],
1340 ['tag', 'config:config-item',
1341 ['element', 'config:name', 'PositionBottom'],
1342 ['element', 'config:type', 'int'],
1344 ['tag', 'config:config-item',
1345 ['element', 'config:name', 'ActiveTable'],
1346 ['element', 'config:type', 'string'],
1347 ['data', 'Sheet1']],
1348 ['tag', 'config:config-item',
1349 ['element', 'config:name', 'HorizontalScrollbarWidth'],
1350 ['element', 'config:type', 'int'],
1352 ['tag', 'config:config-item',
1353 ['element', 'config:name', 'ZoomType'],
1354 ['element', 'config:type', 'short'],
1356 ['tag', 'config:config-item',
1357 ['element', 'config:name', 'ZoomValue'],
1358 ['element', 'config:type', 'int'],
1360 ['tag', 'config:config-item',
1361 ['element', 'config:name', 'PageViewZoomValue'],
1362 ['element', 'config:type', 'int'],
1364 ['tag', 'config:config-item',
1365 ['element', 'config:name', 'ShowPageBreakPreview'],
1366 ['element', 'config:type', 'boolean'],
1368 ['tag', 'config:config-item',
1369 ['element', 'config:name', 'ShowZeroValues'],
1370 ['element', 'config:type', 'boolean'],
1372 ['tag', 'config:config-item',
1373 ['element', 'config:name', 'ShowNotes'],
1374 ['element', 'config:type', 'boolean'],
1376 ['tag', 'config:config-item',
1377 ['element', 'config:name', 'ShowGrid'],
1378 ['element', 'config:type', 'boolean'],
1380 ['tag', 'config:config-item',
1381 ['element', 'config:name', 'GridColor'],
1382 ['element', 'config:type', 'long'],
1383 ['data', '12632256']],
1384 ['tag', 'config:config-item',
1385 ['element', 'config:name', 'ShowPageBreaks'],
1386 ['element', 'config:type', 'boolean'],
1388 ['tag', 'config:config-item',
1389 ['element', 'config:name', 'HasColumnRowHeaders'],
1390 ['element', 'config:type', 'boolean'],
1392 ['tag', 'config:config-item',
1393 ['element', 'config:name', 'HasSheetTabs'],
1394 ['element', 'config:type', 'boolean'],
1396 ['tag', 'config:config-item',
1397 ['element', 'config:name', 'IsOutlineSymbolsSet'],
1398 ['element', 'config:type', 'boolean'],
1400 ['tag', 'config:config-item',
1401 ['element', 'config:name', 'IsSnapToRaster'],
1402 ['element', 'config:type', 'boolean'],
1404 ['tag', 'config:config-item',
1405 ['element', 'config:name', 'RasterIsVisible'],
1406 ['element', 'config:type', 'boolean'],
1408 ['tag', 'config:config-item',
1409 ['element', 'config:name', 'RasterResolutionX'],
1410 ['element', 'config:type', 'int'],
1412 ['tag', 'config:config-item',
1413 ['element', 'config:name', 'RasterResolutionY'],
1414 ['element', 'config:type', 'int'],
1416 ['tag', 'config:config-item',
1417 ['element', 'config:name', 'RasterSubdivisionX'],
1418 ['element', 'config:type', 'int'],
1420 ['tag', 'config:config-item',
1421 ['element', 'config:name', 'RasterSubdivisionY'],
1422 ['element', 'config:type', 'int'],
1424 ['tag', 'config:config-item',
1425 ['element', 'config:name', 'IsRasterAxisSynchronized'],
1426 ['element', 'config:type', 'boolean'],
1427 ['data', 'true']]]]],
1428 ['tag', 'config:config-item-set',
1429 ['element', 'config:name', 'ooo:configuration-settings'],
1430 ['tag', 'config:config-item',
1431 ['element', 'config:name', 'ShowZeroValues'],
1432 ['element', 'config:type', 'boolean'],
1434 ['tag', 'config:config-item',
1435 ['element', 'config:name', 'ShowNotes'],
1436 ['element', 'config:type', 'boolean'],
1438 ['tag', 'config:config-item',
1439 ['element', 'config:name', 'ShowGrid'],
1440 ['element', 'config:type', 'boolean'],
1442 ['tag', 'config:config-item',
1443 ['element', 'config:name', 'GridColor'],
1444 ['element', 'config:type', 'long'],
1445 ['data', '12632256']],
1446 ['tag', 'config:config-item',
1447 ['element', 'config:name', 'ShowPageBreaks'],
1448 ['element', 'config:type', 'boolean'],
1450 ['tag', 'config:config-item',
1451 ['element', 'config:name', 'LinkUpdateMode'],
1452 ['element', 'config:type', 'short'],
1454 ['tag', 'config:config-item',
1455 ['element', 'config:name', 'HasColumnRowHeaders'],
1456 ['element', 'config:type', 'boolean'],
1458 ['tag', 'config:config-item',
1459 ['element', 'config:name', 'HasSheetTabs'],
1460 ['element', 'config:type', 'boolean'],
1462 ['tag', 'config:config-item',
1463 ['element', 'config:name', 'IsOutlineSymbolsSet'],
1464 ['element', 'config:type', 'boolean'],
1466 ['tag', 'config:config-item',
1467 ['element', 'config:name', 'IsSnapToRaster'],
1468 ['element', 'config:type', 'boolean'],
1470 ['tag', 'config:config-item',
1471 ['element', 'config:name', 'RasterIsVisible'],
1472 ['element', 'config:type', 'boolean'],
1474 ['tag', 'config:config-item',
1475 ['element', 'config:name', 'RasterResolutionX'],
1476 ['element', 'config:type', 'int'],
1478 ['tag', 'config:config-item',
1479 ['element', 'config:name', 'RasterResolutionY'],
1480 ['element', 'config:type', 'int'],
1482 ['tag', 'config:config-item',
1483 ['element', 'config:name', 'RasterSubdivisionX'],
1484 ['element', 'config:type', 'int'],
1486 ['tag', 'config:config-item',
1487 ['element', 'config:name', 'RasterSubdivisionY'],
1488 ['element', 'config:type', 'int'],
1490 ['tag', 'config:config-item',
1491 ['element', 'config:name', 'IsRasterAxisSynchronized'],
1492 ['element', 'config:type', 'boolean'],
1494 ['tag', 'config:config-item',
1495 ['element', 'config:name', 'AutoCalculate'],
1496 ['element', 'config:type', 'boolean'],
1498 ['tag', 'config:config-item',
1499 ['element', 'config:name', 'PrinterName'],
1500 ['element', 'config:type', 'string'],
1501 ['data', 'Generic Printer']],
1502 ['tag', 'config:config-item',
1503 ['element', 'config:name', 'PrinterSetup'],
1504 ['element', 'config:type', 'base64Binary'],
1505 ['data', 'YgH+/0dlbmVyaWMgUHJpbnRlcgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU0dFTlBSVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWAAMAqAAAAAAA//8FAFZUAAAkbQAASm9iRGF0YSAxCnByaW50ZXI9R2VuZXJpYyBQcmludGVyCm9yaWVudGF0aW9uPVBvcnRyYWl0CmNvcGllcz0xCnNjYWxlPTEwMAptYXJnaW5kYWp1c3RtZW50PTAsMCwwLDAKY29sb3JkZXB0aD0yNApwc2xldmVsPTAKY29sb3JkZXZpY2U9MApQUERDb250ZXhEYXRhClBhZ2VTaXplOkxldHRlcgAA']],
1506 ['tag', 'config:config-item',
1507 ['element', 'config:name', 'ApplyUserData'],
1508 ['element', 'config:type', 'boolean'],
1510 ['tag', 'config:config-item',
1511 ['element', 'config:name', 'CharacterCompressionType'],
1512 ['element', 'config:type', 'short'],
1514 ['tag', 'config:config-item',
1515 ['element', 'config:name', 'IsKernAsianPunctuation'],
1516 ['element', 'config:type', 'boolean'],
1518 ['tag', 'config:config-item',
1519 ['element', 'config:name', 'SaveVersionOnClose'],
1520 ['element', 'config:type', 'boolean'],
1522 ['tag', 'config:config-item',
1523 ['element', 'config:name', 'UpdateFromTemplate'],
1524 ['element', 'config:type', 'boolean'],
1526 ['tag', 'config:config-item',
1527 ['element', 'config:name', 'AllowPrintJobCancel'],
1528 ['element', 'config:type', 'boolean'],
1530 ['tag', 'config:config-item',
1531 ['element', 'config:name', 'LoadReadonly'],
1532 ['element', 'config:type', 'boolean'],
1533 ['data', 'false']]]]]
1534 # Generate content.xml XML data
1536 self.lines = xml.convert(self.data)
1537 self.filedata = '\n'.join(self.lines)
1538 # Return generated data
1539 return self.filedata
1541 def _ods_styles(self):
1542 "Generate ods styles.xml data"
1543 self.data = ['tag', 'office:document-styles',
1544 ['element', 'xmlns:office', 'urn:oasis:names:tc:opendocument:xmlns:office:1.0'],
1545 ['element', 'xmlns:style', 'urn:oasis:names:tc:opendocument:xmlns:style:1.0'],
1546 ['element', 'xmlns:text', 'urn:oasis:names:tc:opendocument:xmlns:text:1.0'],
1547 ['element', 'xmlns:table', 'urn:oasis:names:tc:opendocument:xmlns:table:1.0'],
1548 ['element', 'xmlns:draw', 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0'],
1549 ['element', 'xmlns:fo', 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0'],
1550 ['element', 'xmlns:xlink', 'http://www.w3.org/1999/xlink'],
1551 ['element', 'xmlns:dc', 'http://purl.org/dc/elements/1.1/'],
1552 ['element', 'xmlns:meta', 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0'],
1553 ['element', 'xmlns:number', 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0'],
1554 ['element', 'xmlns:svg', 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0'],
1555 ['element', 'xmlns:chart', 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0'],
1556 ['element', 'xmlns:dr3d', 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0'],
1557 ['element', 'xmlns:math', 'http://www.w3.org/1998/Math/MathML'],
1558 ['element', 'xmlns:form', 'urn:oasis:names:tc:opendocument:xmlns:form:1.0'],
1559 ['element', 'xmlns:script', 'urn:oasis:names:tc:opendocument:xmlns:script:1.0'],
1560 ['element', 'xmlns:ooo', 'http://openoffice.org/2004/office'],
1561 ['element', 'xmlns:ooow', 'http://openoffice.org/2004/writer'],
1562 ['element', 'xmlns:oooc', 'http://openoffice.org/2004/calc'],
1563 ['element', 'xmlns:dom', 'http://www.w3.org/2001/xml-events'],
1564 ['element', 'office:version', '1.0'],
1565 ['tag', 'office:font-face-decls',
1566 ['tagline', 'style:font-face',
1567 ['element', 'style:name', 'DejaVu Sans'],
1568 ['element', 'svg:font-family', ''DejaVu Sans''],
1569 ['element', 'style:font-pitch', 'variable']],
1570 ['tagline', 'style:font-face',
1571 ['element', 'style:name', 'Nimbus Sans L'],
1572 ['element', 'svg:font-family', ''Nimbus Sans L''],
1573 ['element', 'style:font-family-generic', 'swiss'],
1574 ['element', 'style:font-pitch', 'variable']]],
1575 ['tag', 'office:styles',
1576 ['tag', 'style:default-style',
1577 ['element', 'style:family', 'table-cell'],
1578 ['tagline', 'style:table-cell-properties',
1579 ['element', 'style:decimal-places', '2']],
1580 ['tagline', 'style:paragraph-properties',
1581 ['element', 'style:tab-stop-distance', '0.5in']],
1582 ['tagline', 'style:text-properties',
1583 ['element', 'style:font-name', 'Nimbus Sans L'],
1584 ['element', 'fo:language', 'en'],
1585 ['element', 'fo:country', 'US'],
1586 ['element', 'style:font-name-asian', 'DejaVu Sans'],
1587 ['element', 'style:language-asian', 'none'],
1588 ['element', 'style:country-asian', 'none'],
1589 ['element', 'style:font-name-complex', 'DejaVu Sans'],
1590 ['element', 'style:language-complex', 'none'],
1591 ['element', 'style:country-complex', 'none']]],
1592 ['tag', 'number:number-style',
1593 ['element', 'style:name', 'N0'],
1594 ['tagline', 'number:number',
1595 ['element', 'number:min-integer-digits', '1']]],
1596 ['tag', 'number:currency-style',
1597 ['element', 'style:name', 'N104P0'],
1598 ['element', 'style:volatile', 'true'],
1599 ['tag', 'number:currency-symbol',
1600 ['element', 'number:language', 'en'],
1601 ['element', 'number:country', 'US'],
1603 ['tagline', 'number:number',
1604 ['element', 'number:decimal-places', '2'],
1605 ['element', 'number:min-integer-digits', '1'],
1606 ['element', 'number:grouping', 'true']]],
1607 ['tag', 'number:currency-style',
1608 ['element', 'style:name', 'N104'],
1609 ['tagline', 'style:text-properties',
1610 ['element', 'fo:color', '#ff0000']],
1611 ['tag', 'number:text',
1613 ['tag', 'number:currency-symbol',
1614 ['element', 'number:language', 'en'],
1615 ['element', 'number:country', 'US'],
1617 ['tagline', 'number:number',
1618 ['element', 'number:decimal-places', '2'],
1619 ['element', 'number:min-integer-digits', '1'],
1620 ['element', 'number:grouping', 'true']],
1621 ['tagline', 'style:map',
1622 ['element', 'style:condition', 'value()>=0'],
1623 ['element', 'style:apply-style-name', 'N104P0']]],
1624 ['tagline', 'style:style',
1625 ['element', 'style:name', 'Default'],
1626 ['element', 'style:family', 'table-cell']],
1627 ['tag', 'style:style',
1628 ['element', 'style:name', 'Result'],
1629 ['element', 'style:family', 'table-cell'],
1630 ['element', 'style:parent-style-name', 'Default'],
1631 ['tagline', 'style:text-properties',
1632 ['element', 'fo:font-style', 'italic'],
1633 ['element', 'style:text-underline-style', 'solid'],
1634 ['element', 'style:text-underline-width', 'auto'],
1635 ['element', 'style:text-underline-color', 'font-color'],
1636 ['element', 'fo:font-weight', 'bold']]],
1637 ['tagline', 'style:style',
1638 ['element', 'style:name', 'Result2'],
1639 ['element', 'style:family', 'table-cell'],
1640 ['element', 'style:parent-style-name', 'Result'],
1641 ['element', 'style:data-style-name', 'N104']],
1642 ['tag', 'style:style',
1643 ['element', 'style:name', 'Heading'],
1644 ['element', 'style:family', 'table-cell'],
1645 ['element', 'style:parent-style-name', 'Default'],
1646 ['tagline', 'style:table-cell-properties',
1647 ['element', 'style:text-align-source', 'fix'],
1648 ['element', 'style:repeat-content', 'false']],
1649 ['tagline', 'style:paragraph-properties',
1650 ['element', 'fo:text-align', 'center']],
1651 ['tagline', 'style:text-properties',
1652 ['element', 'fo:font-size', '16pt'],
1653 ['element', 'fo:font-style', 'italic'],
1654 ['element', 'fo:font-weight', 'bold']]],
1655 ['tag', 'style:style',
1656 ['element', 'style:name', 'Heading1'],
1657 ['element', 'style:family', 'table-cell'],
1658 ['element', 'style:parent-style-name', 'Heading'],
1659 ['tagline', 'style:table-cell-properties',
1660 ['element', 'style:rotation-angle', '90']]]],
1661 ['tag', 'office:automatic-styles',
1662 ['tag', 'style:page-layout',
1663 ['element', 'style:name', 'pm1'],
1664 ['tagline', 'style:page-layout-properties',
1665 ['element', 'style:writing-mode', 'lr-tb']],
1666 ['tag', 'style:header-style',
1667 ['tagline', 'style:header-footer-properties',
1668 ['element', 'fo:min-height', '0.2957in'],
1669 ['element', 'fo:margin-left', '0in'],
1670 ['element', 'fo:margin-right', '0in'],
1671 ['element', 'fo:margin-bottom', '0.0984in']]],
1672 ['tag', 'style:footer-style',
1673 ['tagline', 'style:header-footer-properties',
1674 ['element', 'fo:min-height', '0.2957in'],
1675 ['element', 'fo:margin-left', '0in'],
1676 ['element', 'fo:margin-right', '0in'],
1677 ['element', 'fo:margin-top', '0.0984in']]]],
1678 ['tag', 'style:page-layout',
1679 ['element', 'style:name', 'pm2'],
1680 ['tagline', 'style:page-layout-properties',
1681 ['element', 'style:writing-mode', 'lr-tb']],
1682 ['tag', 'style:header-style',
1683 ['tag', 'style:header-footer-properties',
1684 ['element', 'fo:min-height', '0.2957in'],
1685 ['element', 'fo:margin-left', '0in'],
1686 ['element', 'fo:margin-right', '0in'],
1687 ['element', 'fo:margin-bottom', '0.0984in'],
1688 ['element', 'fo:border', '0.0346in solid #000000'],
1689 ['element', 'fo:padding', '0.0071in'],
1690 ['element', 'fo:background-color', '#c0c0c0'],
1691 ['tagline', 'style:background-image']]],
1692 ['tag', 'style:footer-style',
1693 ['tag', 'style:header-footer-properties',
1694 ['element', 'fo:min-height', '0.2957in'],
1695 ['element', 'fo:margin-left', '0in'],
1696 ['element', 'fo:margin-right', '0in'],
1697 ['element', 'fo:margin-top', '0.0984in'],
1698 ['element', 'fo:border', '0.0346in solid #000000'],
1699 ['element', 'fo:padding', '0.0071in'],
1700 ['element', 'fo:background-color', '#c0c0c0'],
1701 ['tagline', 'style:background-image']]]]],
1702 ['tag', 'office:master-styles',
1703 ['tag', 'style:master-page',
1704 ['element', 'style:name', 'Default'],
1705 ['element', 'style:page-layout-name', 'pm1'],
1706 ['tag', 'style:header',
1708 ['data', '<text:sheet-name>???</text:sheet-name>']]],
1709 ['tagline', 'style:header-left',
1710 ['element', 'style:display', 'false']],
1711 ['tag', 'style:footer',
1713 ['data', 'Page <text:page-number>1</text:page-number>']]],
1714 ['tagline', 'style:footer-left',
1715 ['element', 'style:display', 'false']]],
1716 ['tag', 'style:master-page',
1717 ['element', 'style:name', 'Report'],
1718 ['element', 'style:page-layout-name', 'pm2'],
1719 ['tag', 'style:header',
1720 ['tag', 'style:region-left',
1722 ['data', '<text:sheet-name>???</text:sheet-name> (<text:title>???</text:title>)']]],
1723 ['tag', 'style:region-right',
1725 ['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>']]]],
1726 ['tagline', 'style:header-left',
1727 ['element', 'style:display', 'false']],
1728 ['tag', 'style:footer',
1730 ['data', 'Page <text:page-number>1</text:page-number> / <text:page-count>99</text:page-count>']]],
1731 ['tagline', 'style:footer-left',
1732 ['element', 'style:display', 'false']]]]]
1733 # Generate content.xml XML data
1735 self.lines = xml.convert(self.data)
1736 self.filedata = '\n'.join(self.lines)
1737 # Return generated data
1738 return self.filedata
1741 "Writer Class - Used to create OpenDocument Format Writer Documents."
1744 "Initialize ooolib Writer instance"
1745 # Default to no debugging
1747 self.meta = Meta('odt')
1749 def set_meta(self, metaname, value):
1750 "Set meta data in your document."
1751 self.meta.set_meta(metaname, value)
1753 def save(self, filename):
1754 """Save .odt document
1756 The save function saves the current .odt document.
1758 if self.debug: print("Writing %s" % filename)
1759 self.savefile = zipfile.ZipFile(filename, "w")
1760 if self.debug: print(" meta.xml")
1761 self._zip_insert(self.savefile, "meta.xml", self.meta.get_meta())
1762 if self.debug: print(" mimetype")
1763 self._zip_insert(self.savefile, "mimetype", "application/vnd.oasis.opendocument.text")
1764 if self.debug: print(" META-INF/manifest.xml")
1765 self._zip_insert(self.savefile, "META-INF/manifest.xml", self._odt_manifest())
1766 if self.debug: print(" content.xml")
1767 self._zip_insert(self.savefile, "content.xml", self._odt_content())
1768 if self.debug: print(" settings.xml")
1769 # self._zip_insert(self.savefile, "settings.xml", self._odt_settings())
1770 if self.debug: print(" styles.xml")
1771 # self._zip_insert(self.savefile, "styles.xml", self._odt_styles())
1772 # We need to close the file now that we are done creating it.
1773 self.savefile.close()
1775 def _zip_insert(self, file, filename, data):
1776 now = time.localtime(time.time())[:6]
1777 info = zipfile.ZipInfo(filename)
1778 info.date_time = now
1779 info.compress_type = zipfile.ZIP_DEFLATED
1780 file.writestr(info, data)
1782 def _odt_manifest(self):
1783 "Generate odt manifest.xml data"
1784 self.data = ['tag', 'manifest:manifest',
1785 ['element', 'xmlns:manifest', 'urn:oasis:names:tc:opendocument:xmlns:manifest:1.0'],
1786 ['tagline', 'manifest:file-entry',
1787 ['element', 'manifest:media-type', 'application/vnd.oasis.opendocument.text'],
1788 ['element', 'manifest:full-path', '/']],
1789 ['tagline', 'manifest:file-entry',
1790 ['element', 'manifest:media-type', 'text/xml'],
1791 ['element', 'manifest:full-path', 'content.xml']],
1792 ['tagline', 'manifest:file-entry',
1793 ['element', 'manifest:media-type', 'text/xml'],
1794 ['element', 'manifest:full-path', 'styles.xml']],
1795 ['tagline', 'manifest:file-entry',
1796 ['element', 'manifest:media-type', 'text/xml'],
1797 ['element', 'manifest:full-path', 'meta.xml']],
1798 ['tagline', 'manifest:file-entry',
1799 ['element', 'manifest:media-type', 'text/xml'],
1800 ['element', 'manifest:full-path', 'settings.xml']]]
1801 # Generate content.xml XML data
1803 self.lines = xml.convert(self.data)
1804 self.lines.insert(1, '<!DOCTYPE manifest:manifest PUBLIC "-//OpenOffice.org//DTD Manifest 1.0//EN" "Manifest.dtd">')
1805 self.filedata = '\n'.join(self.lines)
1806 # Return generated data
1807 return self.filedata
1809 def _odt_content(self):
1810 "Generate odt content.xml data"
1811 self.data = ['tag', 'office:document-content',
1812 ['element', 'xmlns:office', 'urn:oasis:names:tc:opendocument:xmlns:office:1.0'],
1813 ['element', 'xmlns:style', 'urn:oasis:names:tc:opendocument:xmlns:style:1.0'],
1814 ['element', 'xmlns:text', 'urn:oasis:names:tc:opendocument:xmlns:text:1.0'],
1815 ['element', 'xmlns:table', 'urn:oasis:names:tc:opendocument:xmlns:table:1.0'],
1816 ['element', 'xmlns:draw', 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0'],
1817 ['element', 'xmlns:fo', 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0'],
1818 ['element', 'xmlns:xlink', 'http://www.w3.org/1999/xlink'],
1819 ['element', 'xmlns:dc', 'http://purl.org/dc/elements/1.1/'],
1820 ['element', 'xmlns:meta', 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0'],
1821 ['element', 'xmlns:number', 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0'],
1822 ['element', 'xmlns:svg', 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0'],
1823 ['element', 'xmlns:chart', 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0'],
1824 ['element', 'xmlns:dr3d', 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0'],
1825 ['element', 'xmlns:math', 'http://www.w3.org/1998/Math/MathML'],
1826 ['element', 'xmlns:form', 'urn:oasis:names:tc:opendocument:xmlns:form:1.0'],
1827 ['element', 'xmlns:script', 'urn:oasis:names:tc:opendocument:xmlns:script:1.0'],
1828 ['element', 'xmlns:ooo', 'http://openoffice.org/2004/office'],
1829 ['element', 'xmlns:ooow', 'http://openoffice.org/2004/writer'],
1830 ['element', 'xmlns:oooc', 'http://openoffice.org/2004/calc'],
1831 ['element', 'xmlns:dom', 'http://www.w3.org/2001/xml-events'],
1832 ['element', 'xmlns:xforms', 'http://www.w3.org/2002/xforms'],
1833 ['element', 'xmlns:xsd', 'http://www.w3.org/2001/XMLSchema'],
1834 ['element', 'xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance'],
1835 ['element', 'office:version', '1.0'],
1836 ['tagline', 'office:scripts'],
1837 ['tag', 'office:font-face-decls',
1838 ['tagline', 'style:font-face',
1839 ['element', 'style:name', 'DejaVu Sans'],
1840 ['element', 'svg:font-family', ''DejaVu Sans''],
1841 ['element', 'style:font-pitch', 'variable']],
1842 ['tagline', 'style:font-face',
1843 ['element', 'style:name', 'Nimbus Roman No9 L'],
1844 ['element', 'svg:font-family', ''Nimbus Roman No9 L''],
1845 ['element', 'style:font-family-generic', 'roman'],
1846 ['element', 'style:font-pitch', 'variable']],
1847 ['tagline', 'style:font-face',
1848 ['element', 'style:name', 'Nimbus Sans L'],
1849 ['element', 'svg:font-family', ''Nimbus Sans L''],
1850 ['element', 'style:font-family-generic', 'swiss'],
1851 ['element', 'style:font-pitch', 'variable']]],
1852 ['tagline', 'office:automatic-styles'],
1853 ['tag', 'office:body',
1854 ['tag', 'office:text',
1855 ['tagline', 'office:forms',
1856 ['element', 'form:automatic-focus', 'false'],
1857 ['element', 'form:apply-design-mode', 'false']],
1858 ['tag', 'text:sequence-decls',
1859 ['tagline', 'text:sequence-decl',
1860 ['element', 'text:display-outline-level', '0'],
1861 ['element', 'text:name', 'Illustration']],
1862 ['tagline', 'text:sequence-decl',
1863 ['element', 'text:display-outline-level', '0'],
1864 ['element', 'text:name', 'Table']],
1865 ['tagline', 'text:sequence-decl',
1866 ['element', 'text:display-outline-level', '0'],
1867 ['element', 'text:name', 'Text']],
1868 ['tagline', 'text:sequence-decl',
1869 ['element', 'text:display-outline-level', '0'],
1870 ['element', 'text:name', 'Drawing']]],
1871 ['tagline', 'text:p',
1872 ['element', 'text:style-name', 'Standard']]]]]
1873 # Generate content.xml XML data
1875 self.lines = xml.convert(self.data)
1876 self.filedata = '\n'.join(self.lines)
1877 # Return generated data
1878 return self.filedata