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.major == 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.major==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.major==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.major==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(mediatype)==type(
u""))
456 assert(type(content) == type(b
""))
472 if mediatype
is None:
473 mediatype, encoding = mimetypes.guess_type(filename)
474 if mediatype
is None:
476 try: ext = filename[filename.rindex(
u'.'):]
477 except ValueError: ext=
u''
479 ext = mimetypes.guess_extension(mediatype)
480 manifestfn =
u"Pictures/%0.0f%s" % ((time.time()*10000000000), ext)
481 self.
Pictures[manifestfn] = (IS_FILENAME, filename, mediatype)
483 assert(type(filename)==type(
u""))
484 assert(type(mediatype)==type(
u""))
500 assert(type(content)==type(b
""))
501 assert(type(mediatype)==type(
u""))
503 ext = mimetypes.guess_extension(mediatype)
504 manifestfn =
u"Pictures/%0.0f%s" % ((time.time()*10000000000), ext)
505 self.
Pictures[manifestfn] = (IS_IMAGE, content, mediatype)
515 assert(type(filecontent)==type(b
""))
517 if filecontent
is None:
532 assert(isinstance(document, OpenDocument))
533 assert(type(objectname)==type(
u"")
or objectname ==
None)
535 self.childobjects.append(document)
536 if objectname
is None:
539 document.folder = objectname
540 return u".%s" % document.folder
548 def _savePictures(self, anObject, folder):
549 assert(isinstance(anObject, OpenDocument))
550 assert(type(folder)==type(
u""))
553 for arcname, picturerec
in anObject.Pictures.items():
554 what_it_is, fileobj, mediatype = picturerec
555 self.manifest.addElement(manifest.FileEntry(fullpath=
u"%s%s" % ( folder ,arcname), mediatype=mediatype))
557 if what_it_is == IS_FILENAME:
558 self._z.write(fileobj, arcname, zipfile.ZIP_STORED)
560 zi = zipfile.ZipInfo(str(arcname), self.
_now)
561 zi.compress_type = zipfile.ZIP_STORED
562 zi.external_attr = UNIXPERMS
563 self._z.writestr(zi, fileobj)
569 for subobject
in anObject.childobjects:
570 self.
_savePictures(subobject,
u'%sObject %d/' % (folder, subobjectnum))
580 def __replaceGenerator(self):
581 for m
in self.meta.childNodes[:]:
582 if m.qname == (METANS,
u'generator'):
583 self.meta.removeChild(m)
584 self.meta.addElement(meta.Generator(text=TOOLSVERSION))
595 def save(self, outputfile, addsuffix=False):
596 assert(type(outputfile)==type(
u"")
or 'wb' in repr(outputfile)
or 'BufferedWriter' in repr(outputfile)
or 'BytesIO' in repr(outputfile))
597 assert(type(addsuffix)==type(
True))
599 if outputfile ==
u'-':
600 outputfp = zipfile.ZipFile(sys.stdout,
"w")
603 outputfile = outputfile + odmimetypes.get(self.
mimetype,
u'.xxx')
604 outputfp = zipfile.ZipFile(outputfile,
"w")
615 assert(
'wb' in repr(outputfp)
or 'BufferedWriter' in repr(outputfp)
or 'BytesIO' in repr(outputfp))
617 zipoutputfp = zipfile.ZipFile(outputfp,
"w")
626 def __zipwrite(self, outputfp):
627 assert(isinstance(outputfp, zipfile.ZipFile))
630 self.
_now = time.localtime()[:6]
634 zi = zipfile.ZipInfo(
'mimetype', self.
_now)
635 zi.compress_type = zipfile.ZIP_STORED
636 zi.external_attr = UNIXPERMS
637 self._z.writestr(zi, self.mimetype.encode(
"utf-8"))
646 self.manifest.addElement(manifest.FileEntry(fullpath=
u"Thumbnails/", mediatype=
u''))
647 self.manifest.addElement(manifest.FileEntry(fullpath=
u"Thumbnails/thumbnail.png", mediatype=
u''))
648 zi = zipfile.ZipInfo(
u"Thumbnails/thumbnail.png", self.
_now)
649 zi.compress_type = zipfile.ZIP_DEFLATED
650 zi.external_attr = UNIXPERMS
655 if op.filename ==
u"META-INF/documentsignatures.xml":
continue
656 self.manifest.addElement(manifest.FileEntry(fullpath=op.filename, mediatype=op.mediatype))
657 if sys.version_info.major==3:
658 zi = zipfile.ZipInfo(op.filename, self.
_now)
660 zi = zipfile.ZipInfo(op.filename.encode(
'utf-8'), self.
_now)
661 zi.compress_type = zipfile.ZIP_DEFLATED
662 zi.external_attr = UNIXPERMS
663 if op.content
is not None:
664 self._z.writestr(zi, op.content)
666 zi = zipfile.ZipInfo(
u"META-INF/manifest.xml", self.
_now)
667 zi.compress_type = zipfile.ZIP_DEFLATED
668 zi.external_attr = UNIXPERMS
681 def _saveXmlObjects(self, anObject, folder):
682 assert(isinstance(anObject, OpenDocument))
683 assert(type(folder)==type(
u""))
686 self.manifest.addElement(manifest.FileEntry(fullpath=
u"/", mediatype=anObject.mimetype))
688 self.manifest.addElement(manifest.FileEntry(fullpath=folder, mediatype=anObject.mimetype))
690 self.manifest.addElement(manifest.FileEntry(fullpath=
u"%sstyles.xml" % folder, mediatype=
u"text/xml"))
691 zi = zipfile.ZipInfo(
u"%sstyles.xml" % folder, self.
_now)
692 zi.compress_type = zipfile.ZIP_DEFLATED
693 zi.external_attr = UNIXPERMS
694 self._z.writestr(zi, anObject.stylesxml().encode(
"utf-8") )
697 self.manifest.addElement(manifest.FileEntry(fullpath=
u"%scontent.xml" % folder, mediatype=
u"text/xml"))
698 zi = zipfile.ZipInfo(
u"%scontent.xml" % folder, self.
_now)
699 zi.compress_type = zipfile.ZIP_DEFLATED
700 zi.external_attr = UNIXPERMS
701 self._z.writestr(zi, anObject.contentxml() )
704 if anObject.settings.hasChildNodes():
705 self.manifest.addElement(manifest.FileEntry(fullpath=
u"%ssettings.xml" % folder, mediatype=
u"text/xml"))
706 zi = zipfile.ZipInfo(
u"%ssettings.xml" % folder, self.
_now)
707 zi.compress_type = zipfile.ZIP_DEFLATED
708 zi.external_attr = UNIXPERMS
709 self._z.writestr(zi, anObject.settingsxml() )
713 self.manifest.addElement(manifest.FileEntry(fullpath=
u"meta.xml", mediatype=
u"text/xml"))
714 zi = zipfile.ZipInfo(
u"meta.xml", self.
_now)
715 zi.compress_type = zipfile.ZIP_DEFLATED
716 zi.external_attr = UNIXPERMS
717 self._z.writestr(zi, anObject.metaxml() )
721 for subobject
in anObject.childobjects:
722 self.
_saveXmlObjects(subobject,
u'%sObject %d/' % (folder, subobjectnum))
740 return elt(check_grammar=
False)
749 assert(type(data)==type(
u""))
760 assert(type(data)==type(
u""))
770 assert (type(self.
mimetype)==type(
u""))
781 assert(type(name)==type(
u""))
786 result=self._styles_dict.get(ncname,
None)
800 assert(isinstance (elt, types.FunctionType))
802 obj = elt(check_grammar=
False)
813 result=self.element_dict.get(obj.qname, [])
828 doc =
OpenDocument(
u'application/vnd.oasis.opendocument.chart')
830 doc.body.addElement(doc.chart)
839 doc =
OpenDocument(
u'application/vnd.oasis.opendocument.graphics')
841 doc.body.addElement(doc.drawing)
850 doc =
OpenDocument(
u'application/vnd.oasis.opendocument.image')
852 doc.body.addElement(doc.image)
861 doc =
OpenDocument(
u'application/vnd.oasis.opendocument.presentation')
863 doc.body.addElement(doc.presentation)
872 doc =
OpenDocument(
u'application/vnd.oasis.opendocument.spreadsheet')
874 doc.body.addElement(doc.spreadsheet)
883 doc =
OpenDocument(
u'application/vnd.oasis.opendocument.text')
885 doc.body.addElement(doc.text)
894 doc =
OpenDocument(
u'application/vnd.oasis.opendocument.text-master')
896 doc.body.addElement(doc.text)
907 def __loadxmlparts(z, manifest, doc, objectpath):
908 assert(isinstance(z, zipfile.ZipFile))
909 assert(type(manifest)==type(dict()))
910 assert(isinstance(doc, OpenDocument))
911 assert(type(objectpath)==type(
u""))
913 from load
import LoadParser
914 from xml.sax
import make_parser, handler
916 for xmlfile
in (objectpath+
u'settings.xml', objectpath+
u'meta.xml', objectpath+
u'content.xml', objectpath+
u'styles.xml'):
917 if xmlfile
not in manifest:
922 from xml.sax._exceptions
import SAXParseException
925 xmlpart = z.read(xmlfile).decode(
"utf-8")
926 doc._parsing = xmlfile
928 parser = make_parser()
929 parser.setFeature(handler.feature_namespaces, 1)
930 parser.setContentHandler(LoadParser(doc))
931 parser.setErrorHandler(handler.ErrorHandler())
933 inpsrc = InputSource()
939 xmlpart=__fixXmlPart(xmlpart)
941 inpsrc.setByteStream(BytesIO(xmlpart.encode(
"utf-8")))
944 except KeyError
as v:
pass
945 except SAXParseException:
946 print (
u"====== SAX FAILED TO PARSE ==========\n", xmlpart)
956 def __fixXmlPart(xmlpart):
958 requestedPrefixes = (
u'meta',
u'config',
u'dc',
u'style',
959 u'svg',
u'fo',
u'draw',
u'table',
u'form')
960 for prefix
in requestedPrefixes:
961 if u' xmlns:{prefix}'.format(prefix=prefix)
not in xmlpart:
962 pos=result.index(
u" xmlns:")
963 toInsert=
u' xmlns:{prefix}="urn:oasis:names:tc:opendocument:xmlns:{prefix}:1.0"'.format(prefix=prefix)
964 result=result[:pos]+toInsert+result[pos:]
975 def __detectmimetype(zipfd, odffile):
976 assert(isinstance(zipfd, zipfile.ZipFile))
977 assert(type(odffile)==type(
u"")
or 'rb' in repr(odffile) \
978 or 'BufferedReader' in repr(odffile)
or 'BytesIO' in repr(odffile))
981 mimetype = zipfd.read(
'mimetype').decode(
"utf-8")
986 manifestpart = zipfd.read(
'META-INF/manifest.xml')
988 for mentry,mvalue
in manifest.items():
990 assert(type(mvalue[
'media-type'])==type(
u""))
991 return mvalue[
'media-type']
994 return u'application/vnd.oasis.opendocument.text'
1004 assert(type(odffile)==type(
u"")
or 'rb' in repr(odffile) \
1005 or 'BufferedReader' in repr(odffile)
or 'BytesIO' in repr(odffile))
1007 z = zipfile.ZipFile(odffile)
1008 mimetype = __detectmimetype(z, odffile)
1012 manifestpart = z.read(
'META-INF/manifest.xml')
1014 __loadxmlparts(z, manifest, doc,
u'')
1015 for mentry,mvalue
in manifest.items():
1016 if mentry[:9] ==
u"Pictures/" and len(mentry) > 9:
1017 doc.addPicture(mvalue[
'full-path'], mvalue[
'media-type'], z.read(mentry))
1018 elif mentry ==
u"Thumbnails/thumbnail.png":
1019 doc.addThumbnail(z.read(mentry))
1020 elif mentry
in (
u'settings.xml',
u'meta.xml',
u'content.xml',
u'styles.xml'):
1023 elif mentry[:7] ==
u"Object " and len(mentry) < 11
and mentry[-1] ==
u"/":
1024 subdoc =
OpenDocument(mvalue[
'media-type'], add_generator=
False)
1025 doc.addObject(subdoc,
u"/" + mentry[:-1])
1026 __loadxmlparts(z, manifest, subdoc, mentry)
1027 elif mentry[:7] ==
u"Object ":
1030 if mvalue[
'full-path'][-1] ==
u'/':
1031 doc._extra.append(
OpaqueObject(mvalue[
'full-path'], mvalue[
'media-type'],
None))
1033 doc._extra.append(
OpaqueObject(mvalue[
'full-path'], mvalue[
'media-type'], z.read(mentry)))
1037 b = doc.getElementsByType(Body)
1038 if mimetype[:39] ==
u'application/vnd.oasis.opendocument.text':
1039 doc.text = b[0].firstChild
1040 elif mimetype[:43] ==
u'application/vnd.oasis.opendocument.graphics':
1041 doc.graphics = b[0].firstChild
1042 elif mimetype[:47] ==
u'application/vnd.oasis.opendocument.presentation':
1043 doc.presentation = b[0].firstChild
1044 elif mimetype[:46] ==
u'application/vnd.oasis.opendocument.spreadsheet':
1045 doc.spreadsheet = b[0].firstChild
1046 elif mimetype[:40] ==
u'application/vnd.oasis.opendocument.chart':
1047 doc.chart = b[0].firstChild
1048 elif mimetype[:40] ==
u'application/vnd.oasis.opendocument.image':
1049 doc.image = b[0].firstChild
1050 elif mimetype[:42] ==
u'application/vnd.oasis.opendocument.formula':
1051 doc.formula = b[0].firstChild
def __manifestxml
Generates the manifest.xml file; The self.manifest isn't avaible unless the document is being saved...
just a record to bear a filename, a mediatype and a bytes content
def build_caches
Builds internal caches; called from element.py.
def settingsxml
Generates the settings.xml file.
def createElement
Inconvenient interface to create an element, but follows XML-DOM.
def addObject
Adds an object (subdocument).
def contentxml
Generates the content.xml file.
def getMediaType
Returns the media type.
def __init__
the constructor
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 OpenDocumentText
Creates a text document.
def _saveXmlObjects
save xml objects of an opendocument to some folder
def write
User API to write the ODF file to an open file descriptor Writes the ZIP format.
def __replaceGenerator
Removes a previous 'generator' stance and declares TOOLSVERSION as the new generator.
def OpenDocumentImage
Creates an image document.
def _savePictures
saves pictures contained in an object
def __zipwrite
Write the document to an open file pointer This is where the real work is done.
def load
Load an ODF file into memory.
def OpenDocumentDrawing
Creates a drawing document.
def addPictureFromFile
Add a picture It uses the same convention as OOo, in that it saves the picture in the zipfile in the ...
def addPicture
Add a picture It uses the same convention as OOo, in that it saves the picture in the zipfile in the ...
def OpenDocumentTextMaster
Creates a text master document.
def __init__
the constructor
def xml
Generates the full document as an XML "file".
def __register_stylename
Register a style.
def toXml
converts the document to a valid Xml format.
def metaxml
Generates the meta.xml file.
def save
Save the document under the filename.
Creates a arbitrary element and is intended to be subclassed not used on its own. ...
def getStyleByName
Finds a style object based on the name.
def OpenDocumentPresentation
Creates a presentation document.
def createTextNode
Method to create a text node.
def createCDATASection
Method to create a CDATA section.
def addThumbnail
Add a fixed thumbnail The thumbnail in the library is big, so this is pretty useless.
def stylesxml
Generates the styles.xml file.
def clear_caches
Clears internal caches.
def _parseoneelement
Finds references to style objects in master-styles and add the style name to the style list if not al...
def addPictureFromString
Add a picture from contents given as a Byte string.
def _used_auto_styles
Loop through the masterstyles elements, and find the automatic styles that are used.
def OpenDocumentChart
Creates a chart document.
def getElementsByType
Gets elements based on the type, which is function from text.py, draw.py etc.