25 __doc__=
"""Use OpenDocument to generate your documents.""" 27 import zipfile, time, sys, mimetypes, copy, os.path
30 sys.path.append(os.path.dirname(__file__))
35 from io
import StringIO, BytesIO
37 from namespaces
import *
42 from attrconverters
import make_NCName
43 from xml.sax.xmlreader
import InputSource
44 from odfmanifest
import manifestlist
46 if sys.version_info[0] == 3:
49 __version__= TOOLSVERSION
51 _XMLPROLOGUE =
u"<?xml version='1.0' encoding='UTF-8'?>\n" 61 UNIXPERMS = 2175008768
66 assert sys.version_info[0]>=2
and sys.version_info[1] >= 2
76 u'application/vnd.oasis.opendocument.text':
u'.odt',
77 u'application/vnd.oasis.opendocument.text-template':
u'.ott',
78 u'application/vnd.oasis.opendocument.graphics':
u'.odg',
79 u'application/vnd.oasis.opendocument.graphics-template':
u'.otg',
80 u'application/vnd.oasis.opendocument.presentation':
u'.odp',
81 u'application/vnd.oasis.opendocument.presentation-template':
u'.otp',
82 u'application/vnd.oasis.opendocument.spreadsheet':
u'.ods',
83 u'application/vnd.oasis.opendocument.spreadsheet-template':
u'.ots',
84 u'application/vnd.oasis.opendocument.chart':
u'.odc',
85 u'application/vnd.oasis.opendocument.chart-template':
u'.otc',
86 u'application/vnd.oasis.opendocument.image':
u'.odi',
87 u'application/vnd.oasis.opendocument.image-template':
u'.oti',
88 u'application/vnd.oasis.opendocument.formula':
u'.odf',
89 u'application/vnd.oasis.opendocument.formula-template':
u'.otf',
90 u'application/vnd.oasis.opendocument.text-master':
u'.odm',
91 u'application/vnd.oasis.opendocument.text-web':
u'.oth',
106 def __init__(self, filename, mediatype, content=None):
107 assert(type(filename)==type(
u""))
108 assert(type(mediatype)==type(
u""))
109 assert(type(content)==type(b
"")
or content ==
None)
132 assert(type(mimetype)==type(
u""))
133 assert(isinstance(add_generator,True.__class__))
140 self.topnode.ownerDocument = self
146 self.topnode.addElement(self.
meta)
148 self.meta.addElement(meta.Generator(text=TOOLSVERSION))
150 self.topnode.addElement(self.
scripts)
154 self.topnode.addElement(self.
settings)
156 self.topnode.addElement(self.
styles)
162 self.topnode.addElement(self.
body)
165 if node
is None: node = self.
topnode 167 for e
in node.childNodes:
168 if e.nodeType == element.Node.ELEMENT_NODE:
196 if elt.qname == (STYLENS,
u'style'):
198 styleref = elt.getAttrNS(TEXTNS,
u'style-name')
209 def __register_stylename(self, elt):
212 name = elt.getAttrNS(STYLENS,
u'name')
215 if elt.parentNode.qname
in ((OFFICENS,
u'styles'), (OFFICENS,
u'automatic-styles')):
221 elt.setAttrNS(STYLENS,
u'name', name)
235 assert(type(filename)==type(
u""))
239 if sys.version_info[0]==2:
240 xml.write(_XMLPROLOGUE)
242 xml.write(_XMLPROLOGUE)
243 self.body.toXml(0, xml)
245 result=xml.getvalue()
247 f=codecs.open(filename,
'w', encoding=
'utf-8')
248 f.write(xml.getvalue())
260 if sys.version_info[0]==2:
261 xml.write(_XMLPROLOGUE)
263 xml.write(_XMLPROLOGUE)
264 self.topnode.toXml(0, xml)
265 return xml.getvalue().encode(
"utf-8")
275 xml.write(_XMLPROLOGUE)
277 x.write_open_tag(0, xml)
278 if self.scripts.hasChildNodes():
279 self.scripts.toXml(1, xml)
280 if self.fontfacedecls.hasChildNodes():
281 self.fontfacedecls.toXml(1, xml)
284 if len(stylelist) > 0:
285 a.write_open_tag(1, xml)
288 a.write_close_tag(1, xml)
291 self.body.toXml(1, xml)
292 x.write_close_tag(0, xml)
293 return xml.getvalue().encode(
"utf-8")
301 def __manifestxml(self):
303 xml.write(_XMLPROLOGUE)
304 self.manifest.toXml(0,xml)
305 result=xml.getvalue()
306 assert(type(result)==type(
u""))
317 x.addElement(self.
meta)
319 xml.write(_XMLPROLOGUE)
321 result=xml.getvalue()
322 assert(type(result)==type(
u""))
334 if sys.version_info[0]==2:
335 xml.write(_XMLPROLOGUE)
337 xml.write(_XMLPROLOGUE)
339 result=xml.getvalue()
340 assert(type(result)==type(
u""))
350 def _parseoneelement(self, top, stylenamelist):
351 for e
in top.childNodes:
352 if e.nodeType == element.Node.ELEMENT_NODE:
354 (CHARTNS,
u'style-name'),
355 (DRAWNS,
u'style-name'),
356 (DRAWNS,
u'text-style-name'),
357 (PRESENTATIONNS,
u'style-name'),
358 (STYLENS,
u'data-style-name'),
359 (STYLENS,
u'list-style-name'),
360 (STYLENS,
u'page-layout-name'),
361 (STYLENS,
u'style-name'),
362 (TABLENS,
u'default-cell-style-name'),
363 (TABLENS,
u'style-name'),
364 (TEXTNS,
u'style-name') ):
365 if e.getAttrNS(styleref[0],styleref[1]):
366 stylename = e.getAttrNS(styleref[0],styleref[1])
367 if stylename
not in stylenamelist:
370 stylenamelist.append(
unicode(stylename))
381 def _used_auto_styles(self, segments):
386 for e
in self.automaticstyles.childNodes:
387 if e.getAttrNS(STYLENS,
u'name')
in stylenamelist:
404 xml.write(_XMLPROLOGUE)
406 x.write_open_tag(0, xml)
407 if self.fontfacedecls.hasChildNodes():
408 self.fontfacedecls.toXml(1, xml)
409 self.styles.toXml(1, xml)
411 a.write_open_tag(1, xml)
414 a.write_close_tag(1, xml)
415 if self.masterstyles.hasChildNodes():
416 self.masterstyles.toXml(1, xml)
417 x.write_close_tag(0, xml)
418 result = xml.getvalue()
420 assert(type(result)==type(
u""))
436 def addPicture(self, filename, mediatype=None, content=None):
438 if mediatype
is None:
439 mediatype, encoding = mimetypes.guess_type(filename)
440 if mediatype
is None:
442 try: ext = filename[filename.rindex(
u'.'):]
445 ext = mimetypes.guess_extension(mediatype)
446 manifestfn =
u"Pictures/%0.0f%s" % ((time.time()*10000000000), ext)
447 self.
Pictures[manifestfn] = (IS_FILENAME, filename, mediatype)
451 manifestfn = filename
452 self.
Pictures[manifestfn] = (IS_IMAGE, content, mediatype)
454 assert(type(filename)==type(
u""))
455 assert(type(content) == type(b
""))
471 if mediatype
is None:
472 mediatype, encoding = mimetypes.guess_type(filename)
473 if mediatype
is None:
475 try: ext = filename[filename.rindex(
u'.'):]
476 except ValueError: ext=
u'' 478 ext = mimetypes.guess_extension(mediatype)
479 manifestfn =
u"Pictures/%0.0f%s" % ((time.time()*10000000000), ext)
480 self.
Pictures[manifestfn] = (IS_FILENAME, filename, mediatype)
482 assert(type(filename)==type(
u""))
483 assert(type(mediatype)==type(
u""))
499 assert(type(content)==type(b
""))
500 assert(type(mediatype)==type(
u""))
502 ext = mimetypes.guess_extension(mediatype)
503 manifestfn =
u"Pictures/%0.0f%s" % ((time.time()*10000000000), ext)
504 self.
Pictures[manifestfn] = (IS_IMAGE, content, mediatype)
514 assert(type(filecontent)==type(b
""))
516 if filecontent
is None:
531 assert(isinstance(document, OpenDocument))
532 assert(type(objectname)==type(
u"")
or objectname ==
None)
534 self.childobjects.append(document)
535 if objectname
is None:
538 document.folder = objectname
539 return u".%s" % document.folder
547 def _savePictures(self, anObject, folder):
548 assert(isinstance(anObject, OpenDocument))
549 assert(type(folder)==type(
u""))
552 for arcname, picturerec
in anObject.Pictures.items():
553 what_it_is, fileobj, mediatype = picturerec
554 self.manifest.addElement(manifest.FileEntry(fullpath=
u"%s%s" % ( folder ,arcname), mediatype=mediatype))
556 if what_it_is == IS_FILENAME:
557 self._z.write(fileobj, arcname, zipfile.ZIP_STORED)
559 zi = zipfile.ZipInfo(str(arcname), self.
_now)
560 zi.compress_type = zipfile.ZIP_STORED
561 zi.external_attr = UNIXPERMS
562 self._z.writestr(zi, fileobj)
568 for subobject
in anObject.childobjects:
569 self.
_savePictures(subobject,
u'%sObject %d/' % (folder, subobjectnum))
579 def __replaceGenerator(self):
580 for m
in self.meta.childNodes[:]:
581 if m.qname == (METANS,
u'generator'):
582 self.meta.removeChild(m)
583 self.meta.addElement(meta.Generator(text=TOOLSVERSION))
594 def save(self, outputfile, addsuffix=False):
595 assert(type(outputfile)==type(
u"")
or 'wb' in repr(outputfile)
or 'BufferedWriter' in repr(outputfile)
or 'BytesIO' in repr(outputfile))
596 assert(type(addsuffix)==type(
True))
598 if outputfile ==
u'-':
599 outputfp = zipfile.ZipFile(sys.stdout,
"w")
602 outputfile = outputfile + odmimetypes.get(self.
mimetype,
u'.xxx')
603 outputfp = zipfile.ZipFile(outputfile,
"w")
614 assert(
'wb' in repr(outputfp)
or 'BufferedWriter' in repr(outputfp)
or 'BytesIO' in repr(outputfp))
616 zipoutputfp = zipfile.ZipFile(outputfp,
"w")
625 def __zipwrite(self, outputfp):
626 assert(isinstance(outputfp, zipfile.ZipFile))
629 self.
_now = time.localtime()[:6]
633 zi = zipfile.ZipInfo(
'mimetype', self.
_now)
634 zi.compress_type = zipfile.ZIP_STORED
635 zi.external_attr = UNIXPERMS
636 self._z.writestr(zi, self.mimetype.encode(
"utf-8"))
645 self.manifest.addElement(manifest.FileEntry(fullpath=
u"Thumbnails/", mediatype=
u''))
646 self.manifest.addElement(manifest.FileEntry(fullpath=
u"Thumbnails/thumbnail.png", mediatype=
u''))
647 zi = zipfile.ZipInfo(
u"Thumbnails/thumbnail.png", self.
_now)
648 zi.compress_type = zipfile.ZIP_DEFLATED
649 zi.external_attr = UNIXPERMS
654 if op.filename ==
u"META-INF/documentsignatures.xml":
continue 655 self.manifest.addElement(manifest.FileEntry(fullpath=op.filename, mediatype=op.mediatype))
656 if sys.version_info[0]==3:
657 zi = zipfile.ZipInfo(op.filename, self.
_now)
659 zi = zipfile.ZipInfo(op.filename.encode(
'utf-8'), self.
_now)
660 zi.compress_type = zipfile.ZIP_DEFLATED
661 zi.external_attr = UNIXPERMS
662 if op.content
is not None:
663 self._z.writestr(zi, op.content)
665 zi = zipfile.ZipInfo(
u"META-INF/manifest.xml", self.
_now)
666 zi.compress_type = zipfile.ZIP_DEFLATED
667 zi.external_attr = UNIXPERMS
680 def _saveXmlObjects(self, anObject, folder):
681 assert(isinstance(anObject, OpenDocument))
682 assert(type(folder)==type(
u""))
685 self.manifest.addElement(manifest.FileEntry(fullpath=
u"/", mediatype=anObject.mimetype))
687 self.manifest.addElement(manifest.FileEntry(fullpath=folder, mediatype=anObject.mimetype))
689 self.manifest.addElement(manifest.FileEntry(fullpath=
u"%sstyles.xml" % folder, mediatype=
u"text/xml"))
690 zi = zipfile.ZipInfo(
u"%sstyles.xml" % folder, self.
_now)
691 zi.compress_type = zipfile.ZIP_DEFLATED
692 zi.external_attr = UNIXPERMS
693 self._z.writestr(zi, anObject.stylesxml().encode(
"utf-8") )
696 self.manifest.addElement(manifest.FileEntry(fullpath=
u"%scontent.xml" % folder, mediatype=
u"text/xml"))
697 zi = zipfile.ZipInfo(
u"%scontent.xml" % folder, self.
_now)
698 zi.compress_type = zipfile.ZIP_DEFLATED
699 zi.external_attr = UNIXPERMS
700 self._z.writestr(zi, anObject.contentxml() )
703 if anObject.settings.hasChildNodes():
704 self.manifest.addElement(manifest.FileEntry(fullpath=
u"%ssettings.xml" % folder, mediatype=
u"text/xml"))
705 zi = zipfile.ZipInfo(
u"%ssettings.xml" % folder, self.
_now)
706 zi.compress_type = zipfile.ZIP_DEFLATED
707 zi.external_attr = UNIXPERMS
708 self._z.writestr(zi, anObject.settingsxml() )
712 self.manifest.addElement(manifest.FileEntry(fullpath=
u"meta.xml", mediatype=
u"text/xml"))
713 zi = zipfile.ZipInfo(
u"meta.xml", self.
_now)
714 zi.compress_type = zipfile.ZIP_DEFLATED
715 zi.external_attr = UNIXPERMS
716 self._z.writestr(zi, anObject.metaxml() )
720 for subobject
in anObject.childobjects:
721 self.
_saveXmlObjects(subobject,
u'%sObject %d/' % (folder, subobjectnum))
739 return elt(check_grammar=
False)
748 assert(type(data)==type(
u""))
759 assert(type(data)==type(
u""))
769 assert (type(self.
mimetype)==type(
u""))
780 assert(type(name)==type(
u""))
785 result=self._styles_dict.get(ncname,
None)
799 assert(isinstance (elt, types.FunctionType))
801 obj = elt(check_grammar=
False)
812 result=self.element_dict.get(obj.qname, [])
827 doc =
OpenDocument(
u'application/vnd.oasis.opendocument.chart')
829 doc.body.addElement(doc.chart)
838 doc =
OpenDocument(
u'application/vnd.oasis.opendocument.graphics')
840 doc.body.addElement(doc.drawing)
849 doc =
OpenDocument(
u'application/vnd.oasis.opendocument.image')
851 doc.body.addElement(doc.image)
860 doc =
OpenDocument(
u'application/vnd.oasis.opendocument.presentation')
862 doc.body.addElement(doc.presentation)
871 doc =
OpenDocument(
u'application/vnd.oasis.opendocument.spreadsheet')
873 doc.body.addElement(doc.spreadsheet)
882 doc =
OpenDocument(
u'application/vnd.oasis.opendocument.text')
884 doc.body.addElement(doc.text)
893 doc =
OpenDocument(
u'application/vnd.oasis.opendocument.text-master')
895 doc.body.addElement(doc.text)
906 def __loadxmlparts(z, manifest, doc, objectpath):
907 assert(isinstance(z, zipfile.ZipFile))
908 assert(type(manifest)==type(dict()))
909 assert(isinstance(doc, OpenDocument))
910 assert(type(objectpath)==type(
u""))
912 from load
import LoadParser
913 from xml.sax
import make_parser, handler
915 for xmlfile
in (objectpath+
u'settings.xml', objectpath+
u'meta.xml', objectpath+
u'content.xml', objectpath+
u'styles.xml'):
916 if xmlfile
not in manifest:
921 from xml.sax._exceptions
import SAXParseException
924 xmlpart = z.read(xmlfile).decode(
"utf-8")
925 doc._parsing = xmlfile
927 parser = make_parser()
928 parser.setFeature(handler.feature_namespaces, 1)
929 parser.setContentHandler(LoadParser(doc))
930 parser.setErrorHandler(handler.ErrorHandler())
932 inpsrc = InputSource()
938 xmlpart=__fixXmlPart(xmlpart)
940 inpsrc.setByteStream(BytesIO(xmlpart.encode(
"utf-8")))
943 except KeyError
as v:
pass 944 except SAXParseException:
945 print (
u"====== SAX FAILED TO PARSE ==========\n", xmlpart)
955 def __fixXmlPart(xmlpart):
957 requestedPrefixes = (
u'meta',
u'config',
u'dc',
u'style',
958 u'svg',
u'fo',
u'draw',
u'table',
u'form')
959 for prefix
in requestedPrefixes:
960 if u' xmlns:{prefix}'.format(prefix=prefix)
not in xmlpart:
967 pos=result.index(
u" xmlns:")
968 toInsert=
u' xmlns:{prefix}="urn:oasis:names:tc:opendocument:xmlns:{prefix}:1.0"'.format(prefix=prefix)
969 result=result[:pos]+toInsert+result[pos:]
982 def __detectmimetype(zipfd, odffile):
983 assert(isinstance(zipfd, zipfile.ZipFile))
984 assert(type(odffile)==type(
u"")
or 'rb' in repr(odffile) \
985 or 'BufferedReader' in repr(odffile)
or 'BytesIO' in repr(odffile))
988 mimetype = zipfd.read(
'mimetype').decode(
"utf-8")
993 manifestpart = zipfd.read(
'META-INF/manifest.xml')
995 for mentry,mvalue
in manifest.items():
997 assert(type(mvalue[
'media-type'])==type(
u""))
998 return mvalue[
'media-type']
1001 return u'application/vnd.oasis.opendocument.text' 1011 assert(type(odffile)==type(
u"")
or 'rb' in repr(odffile) \
1012 or 'BufferedReader' in repr(odffile)
or 'BytesIO' in repr(odffile))
1014 z = zipfile.ZipFile(odffile)
1015 mimetype = __detectmimetype(z, odffile)
1019 manifestpart = z.read(
'META-INF/manifest.xml')
1021 __loadxmlparts(z, manifest, doc,
u'')
1022 for mentry,mvalue
in manifest.items():
1023 if mentry[:9] ==
u"Pictures/" and len(mentry) > 9:
1024 doc.addPicture(mvalue[
'full-path'], mvalue[
'media-type'], z.read(mentry))
1025 elif mentry ==
u"Thumbnails/thumbnail.png":
1026 doc.addThumbnail(z.read(mentry))
1027 elif mentry
in (
u'settings.xml',
u'meta.xml',
u'content.xml',
u'styles.xml'):
1030 elif mentry[:7] ==
u"Object " and len(mentry) < 11
and mentry[-1] ==
u"/":
1031 subdoc =
OpenDocument(mvalue[
'media-type'], add_generator=
False)
1032 doc.addObject(subdoc,
u"/" + mentry[:-1])
1033 __loadxmlparts(z, manifest, subdoc, mentry)
1034 elif mentry[:7] ==
u"Object ":
1037 if mvalue[
'full-path'][-1] ==
u'/':
1038 doc._extra.append(
OpaqueObject(mvalue[
'full-path'], mvalue[
'media-type'],
None))
1040 doc._extra.append(
OpaqueObject(mvalue[
'full-path'], mvalue[
'media-type'], z.read(mentry)))
1044 b = doc.getElementsByType(Body)
1045 if mimetype[:39] ==
u'application/vnd.oasis.opendocument.text':
1046 doc.text = b[0].firstChild
1047 elif mimetype[:43] ==
u'application/vnd.oasis.opendocument.graphics':
1048 doc.graphics = b[0].firstChild
1049 elif mimetype[:47] ==
u'application/vnd.oasis.opendocument.presentation':
1050 doc.presentation = b[0].firstChild
1051 elif mimetype[:46] ==
u'application/vnd.oasis.opendocument.spreadsheet':
1052 doc.spreadsheet = b[0].firstChild
1053 elif mimetype[:40] ==
u'application/vnd.oasis.opendocument.chart':
1054 doc.chart = b[0].firstChild
1055 elif mimetype[:40] ==
u'application/vnd.oasis.opendocument.image':
1056 doc.image = b[0].firstChild
1057 elif mimetype[:42] ==
u'application/vnd.oasis.opendocument.formula':
1058 doc.formula = b[0].firstChild
def addObject(self, document, objectname=None)
Adds an object (subdocument).
just a record to bear a filename, a mediatype and a bytes content
def DocumentSettings(version="1.2", args)
def OpenDocumentDrawing()
Creates a drawing document.
def createTextNode(self, data)
Method to create a text node.
def Document(version="1.2", args)
A class to hold the content of an OpenDocument document Use the xml method to write the XML source to...
def OpenDocumentSpreadsheet()
Creates a spreadsheet document.
def addPicture(self, filename, mediatype=None, content=None)
Add a picture It uses the same convention as OOo, in that it saves the picture in the zipfile in the ...
def toXml(self, filename=u'')
converts the document to a valid Xml format.
def OpenDocumentPresentation()
Creates a presentation document.
def metaxml(self)
Generates the meta.xml file.
def contentxml(self)
Generates the content.xml file.
def _saveXmlObjects(self, anObject, folder)
save xml objects of an opendocument to some folder
def addPictureFromString(self, content, mediatype)
Add a picture from contents given as a Byte string.
def __zipwrite(self, outputfp)
Write the document to an open file pointer This is where the real work is done.
def getStyleByName(self, name)
Finds a style object based on the name.
def __register_stylename(self, elt)
Register a style.
def rebuild_caches(self, node=None)
def AutomaticStyles(args)
def stylesxml(self)
Generates the styles.xml file.
def OpenDocumentImage()
Creates an image document.
def addThumbnail(self, filecontent=None)
Add a fixed thumbnail The thumbnail in the library is big, so this is pretty useless.
def save(self, outputfile, addsuffix=False)
Save the document under the filename.
def load(odffile)
Load an ODF file into memory.
def clear_caches(self)
Clears internal caches.
def DocumentStyles(version="1.2", args)
def DocumentMeta(version="1.2", args)
def __manifestxml(self)
Generates the manifest.xml file; The self.manifest isn't avaible unless the document is being saved...
def write(self, outputfp)
User API to write the ODF file to an open file descriptor Writes the ZIP format.
def addPictureFromFile(self, filename, mediatype=None)
Add a picture It uses the same convention as OOo, in that it saves the picture in the zipfile in the ...
def build_caches(self, elt)
Builds internal caches; called from element.py.
def OpenDocumentText()
Creates a text document.
def OpenDocumentTextMaster()
Creates a text master document.
def getMediaType(self)
Returns the media type.
def _savePictures(self, anObject, folder)
saves pictures contained in an object
def OpenDocumentChart()
Creates a chart document.
def __replaceGenerator(self)
Removes a previous 'generator' stance and declares TOOLSVERSION as the new generator.
def manifestlist(manifestxml)
def __init__(self, filename, mediatype, content=None)
the constructor
def _used_auto_styles(self, segments)
Loop through the masterstyles elements, and find the automatic styles that are used.
def _parseoneelement(self, top, stylenamelist)
Finds references to style objects in master-styles and add the style name to the style list if not al...
def createCDATASection(self, data)
Method to create a CDATA section.
def xml(self)
Generates the full document as an XML "file".
Creates a arbitrary element and is intended to be subclassed not used on its own. ...
def createElement(self, elt)
Inconvenient interface to create an element, but follows XML-DOM.
def __init__(self, mimetype, add_generator=True)
the constructor
def getElementsByType(self, elt)
Gets elements based on the type, which is function from text.py, draw.py etc.
def settingsxml(self)
Generates the settings.xml file.
def DocumentContent(version="1.2", args)