25 __doc__=
"""Use OpenDocument to generate your documents.""" 27 import zipfile, time, uuid, sys, mimetypes, copy, os.path
30 sys.path.append(os.path.dirname(__file__))
35 from io
import StringIO, BytesIO
43 from xml.sax.xmlreader
import InputSource
47 if sys.version_info[0] == 3:
50 __version__= TOOLSVERSION
52 _XMLPROLOGUE =
u"<?xml version='1.0' encoding='UTF-8'?>\n" 62 UNIXPERMS = 2175008768
67 assert sys.version_info[0]>=2
and sys.version_info[1] >= 2
77 u'application/vnd.oasis.opendocument.text':
u'.odt',
78 u'application/vnd.oasis.opendocument.text-template':
u'.ott',
79 u'application/vnd.oasis.opendocument.graphics':
u'.odg',
80 u'application/vnd.oasis.opendocument.graphics-template':
u'.otg',
81 u'application/vnd.oasis.opendocument.presentation':
u'.odp',
82 u'application/vnd.oasis.opendocument.presentation-template':
u'.otp',
83 u'application/vnd.oasis.opendocument.spreadsheet':
u'.ods',
84 u'application/vnd.oasis.opendocument.spreadsheet-template':
u'.ots',
85 u'application/vnd.oasis.opendocument.chart':
u'.odc',
86 u'application/vnd.oasis.opendocument.chart-template':
u'.otc',
87 u'application/vnd.oasis.opendocument.image':
u'.odi',
88 u'application/vnd.oasis.opendocument.image-template':
u'.oti',
89 u'application/vnd.oasis.opendocument.formula':
u'.odf',
90 u'application/vnd.oasis.opendocument.formula-template':
u'.otf',
91 u'application/vnd.oasis.opendocument.text-master':
u'.odm',
92 u'application/vnd.oasis.opendocument.text-web':
u'.oth',
107 def __init__(self, filename, mediatype, content=None):
108 assert(type(filename)==type(
u""))
109 assert(type(mediatype)==type(
u""))
110 assert(type(content)==type(b
"")
or content ==
None)
133 assert(type(mimetype)==type(
u""))
134 assert(isinstance(add_generator,True.__class__))
141 self.
topnode.ownerDocument = self
149 self.
meta.addElement(meta.Generator(text=TOOLSVERSION))
166 if node
is None: node = self.
topnode 168 for e
in node.childNodes:
169 if e.nodeType == element.Node.ELEMENT_NODE:
197 if elt.qname == (STYLENS,
u'style'):
199 styleref = elt.getAttrNS(TEXTNS,
u'style-name')
210 def __register_stylename(self, elt):
213 name = elt.getAttrNS(STYLENS,
u'name')
216 if elt.parentNode.qname
in ((OFFICENS,
u'styles'), (OFFICENS,
u'automatic-styles')):
222 elt.setAttrNS(STYLENS,
u'name', name)
236 assert(type(filename)==type(
u""))
240 if sys.version_info[0]==2:
241 xml.write(_XMLPROLOGUE)
243 xml.write(_XMLPROLOGUE)
246 result=xml.getvalue()
248 f=codecs.open(filename,
'w', encoding=
'utf-8')
249 f.write(xml.getvalue())
261 if sys.version_info[0]==2:
262 xml.write(_XMLPROLOGUE)
264 xml.write(_XMLPROLOGUE)
266 return xml.getvalue().encode(
"utf-8")
276 xml.write(_XMLPROLOGUE)
278 x.write_open_tag(0, xml)
279 if self.
scripts.hasChildNodes():
285 if len(stylelist) > 0:
286 a.write_open_tag(1, xml)
289 a.write_close_tag(1, xml)
293 x.write_close_tag(0, xml)
294 return xml.getvalue().encode(
"utf-8")
302 def __manifestxml(self):
304 xml.write(_XMLPROLOGUE)
306 result=xml.getvalue()
307 assert(type(result)==type(
u""))
318 x.addElement(self.
meta)
320 xml.write(_XMLPROLOGUE)
322 result=xml.getvalue()
323 assert(type(result)==type(
u""))
335 if sys.version_info[0]==2:
336 xml.write(_XMLPROLOGUE)
338 xml.write(_XMLPROLOGUE)
340 result=xml.getvalue()
341 assert(type(result)==type(
u""))
351 def _parseoneelement(self, top, stylenamelist):
352 for e
in top.childNodes:
353 if e.nodeType == element.Node.ELEMENT_NODE:
355 (CHARTNS,
u'style-name'),
356 (DRAWNS,
u'style-name'),
357 (DRAWNS,
u'text-style-name'),
358 (PRESENTATIONNS,
u'style-name'),
359 (STYLENS,
u'data-style-name'),
360 (STYLENS,
u'list-style-name'),
361 (STYLENS,
u'page-layout-name'),
362 (STYLENS,
u'style-name'),
363 (TABLENS,
u'default-cell-style-name'),
364 (TABLENS,
u'style-name'),
365 (TEXTNS,
u'style-name') ):
366 if e.getAttrNS(styleref[0],styleref[1]):
367 stylename = e.getAttrNS(styleref[0],styleref[1])
368 if stylename
not in stylenamelist:
371 stylenamelist.append(
unicode(stylename))
382 def _used_auto_styles(self, segments):
388 if isinstance(e,
element.Element)
and e.getAttrNS(STYLENS,
u'name')
in stylenamelist:
405 xml.write(_XMLPROLOGUE)
407 x.write_open_tag(0, xml)
412 a.write_open_tag(1, xml)
415 a.write_close_tag(1, xml)
418 x.write_close_tag(0, xml)
419 result = xml.getvalue()
421 assert(type(result)==type(
u""))
437 def addPicture(self, filename, mediatype=None, content=None):
439 if mediatype
is None:
440 mediatype, encoding = mimetypes.guess_type(filename)
441 if mediatype
is None:
443 try: ext = filename[filename.rindex(
u'.'):]
446 ext = mimetypes.guess_extension(mediatype)
447 manifestfn =
u"Pictures/%s%s" % (uuid.uuid4().hex.upper(), ext)
448 self.
Pictures[manifestfn] = (IS_FILENAME, filename, mediatype)
452 manifestfn = filename
453 self.
Pictures[manifestfn] = (IS_IMAGE, content, mediatype)
455 assert(type(filename)==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/%s%s" % (uuid.uuid4().hex.upper(), 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/%s%s" % (uuid.uuid4().hex.upper(), 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)
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, folder + 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):
597 if outputfile ==
u'-':
598 outputfp = zipfile.ZipFile(sys.stdout,
"w")
601 outputfile = outputfile + odmimetypes.get(self.
mimetype,
u'.xxx')
602 outputfp = zipfile.ZipFile(outputfile,
"w")
613 zipoutputfp = zipfile.ZipFile(outputfp,
"w")
622 def __zipwrite(self, outputfp):
623 assert(isinstance(outputfp, zipfile.ZipFile))
626 self.
_now = time.localtime()[:6]
630 zi = zipfile.ZipInfo(
'mimetype', self.
_now)
631 zi.compress_type = zipfile.ZIP_STORED
632 zi.external_attr = UNIXPERMS
633 self.
_z.writestr(zi, self.
mimetype.encode(
"utf-8"))
642 self.
manifest.addElement(manifest.FileEntry(fullpath=
u"Thumbnails/", mediatype=
u''))
643 self.
manifest.addElement(manifest.FileEntry(fullpath=
u"Thumbnails/thumbnail.png", mediatype=
u''))
644 zi = zipfile.ZipInfo(
u"Thumbnails/thumbnail.png", self.
_now)
645 zi.compress_type = zipfile.ZIP_DEFLATED
646 zi.external_attr = UNIXPERMS
651 if op.filename ==
u"META-INF/documentsignatures.xml":
continue 652 self.
manifest.addElement(manifest.FileEntry(fullpath=op.filename, mediatype=op.mediatype))
653 if sys.version_info[0]==3:
654 zi = zipfile.ZipInfo(op.filename, self.
_now)
656 zi = zipfile.ZipInfo(op.filename.encode(
'utf-8'), self.
_now)
657 zi.compress_type = zipfile.ZIP_DEFLATED
658 zi.external_attr = UNIXPERMS
659 if op.content
is not None:
660 self.
_z.writestr(zi, op.content)
662 zi = zipfile.ZipInfo(
u"META-INF/manifest.xml", self.
_now)
663 zi.compress_type = zipfile.ZIP_DEFLATED
664 zi.external_attr = UNIXPERMS
677 def _saveXmlObjects(self, anObject, folder):
678 assert(isinstance(anObject, OpenDocument))
679 assert(type(folder)==type(
u""))
682 self.
manifest.addElement(manifest.FileEntry(fullpath=
u"/", mediatype=anObject.mimetype))
684 self.
manifest.addElement(manifest.FileEntry(fullpath=folder, mediatype=anObject.mimetype))
686 self.
manifest.addElement(manifest.FileEntry(fullpath=
u"%sstyles.xml" % folder, mediatype=
u"text/xml"))
687 zi = zipfile.ZipInfo(
u"%sstyles.xml" % folder, self.
_now)
688 zi.compress_type = zipfile.ZIP_DEFLATED
689 zi.external_attr = UNIXPERMS
690 self.
_z.writestr(zi, anObject.stylesxml().encode(
"utf-8") )
693 self.
manifest.addElement(manifest.FileEntry(fullpath=
u"%scontent.xml" % folder, mediatype=
u"text/xml"))
694 zi = zipfile.ZipInfo(
u"%scontent.xml" % folder, self.
_now)
695 zi.compress_type = zipfile.ZIP_DEFLATED
696 zi.external_attr = UNIXPERMS
697 self.
_z.writestr(zi, anObject.contentxml() )
700 if anObject.settings.hasChildNodes():
701 self.
manifest.addElement(manifest.FileEntry(fullpath=
u"%ssettings.xml" % folder, mediatype=
u"text/xml"))
702 zi = zipfile.ZipInfo(
u"%ssettings.xml" % folder, self.
_now)
703 zi.compress_type = zipfile.ZIP_DEFLATED
704 zi.external_attr = UNIXPERMS
705 self.
_z.writestr(zi, anObject.settingsxml().encode(
"utf-8") )
709 self.
manifest.addElement(manifest.FileEntry(fullpath=
u"meta.xml", mediatype=
u"text/xml"))
710 zi = zipfile.ZipInfo(
u"meta.xml", self.
_now)
711 zi.compress_type = zipfile.ZIP_DEFLATED
712 zi.external_attr = UNIXPERMS
713 self.
_z.writestr(zi, anObject.metaxml().encode(
"utf-8") )
717 for subobject
in anObject.childobjects:
718 self.
_saveXmlObjects(subobject,
u'%sObject %d/' % (folder, subobjectnum))
736 return elt(check_grammar=
False)
745 assert(type(data)==type(
u""))
756 assert(type(data)==type(
u""))
766 assert (type(self.
mimetype)==type(
u""))
777 assert(type(name)==type(
u""))
796 assert(isinstance (elt, types.FunctionType))
798 obj = elt(check_grammar=
False)
824 doc =
OpenDocument(
u'application/vnd.oasis.opendocument.chart')
826 doc.body.addElement(doc.chart)
835 doc =
OpenDocument(
u'application/vnd.oasis.opendocument.graphics')
837 doc.body.addElement(doc.drawing)
846 doc =
OpenDocument(
u'application/vnd.oasis.opendocument.image')
848 doc.body.addElement(doc.image)
857 doc =
OpenDocument(
u'application/vnd.oasis.opendocument.presentation')
859 doc.body.addElement(doc.presentation)
868 doc =
OpenDocument(
u'application/vnd.oasis.opendocument.spreadsheet')
870 doc.body.addElement(doc.spreadsheet)
879 doc =
OpenDocument(
u'application/vnd.oasis.opendocument.text')
881 doc.body.addElement(doc.text)
890 doc =
OpenDocument(
u'application/vnd.oasis.opendocument.text-master')
892 doc.body.addElement(doc.text)
903 def __loadxmlparts(z, manifest, doc, objectpath):
904 assert(isinstance(z, zipfile.ZipFile))
905 assert(type(manifest)==type(dict()))
906 assert(isinstance(doc, OpenDocument))
907 assert(type(objectpath)==type(
u""))
910 from defusedxml.sax
import make_parser
911 from xml.sax
import handler
913 for xmlfile
in (objectpath+
u'settings.xml', objectpath+
u'meta.xml', objectpath+
u'content.xml', objectpath+
u'styles.xml'):
914 if xmlfile
not in manifest:
919 from xml.sax._exceptions
import SAXParseException
922 xmlpart = z.read(xmlfile).decode(
"utf-8")
923 doc._parsing = xmlfile
925 parser = make_parser()
926 parser.setFeature(handler.feature_namespaces, 1)
927 parser.setFeature(handler.feature_external_ges, 0)
928 parser.setContentHandler(LoadParser(doc))
929 parser.setErrorHandler(handler.ErrorHandler())
931 inpsrc = InputSource()
937 xmlpart=__fixXmlPart(xmlpart)
939 inpsrc.setByteStream(BytesIO(xmlpart.encode(
"utf-8")))
942 except KeyError
as v:
pass 943 except SAXParseException:
944 print (
u"====== SAX FAILED TO PARSE ==========\n", xmlpart)
954 def __fixXmlPart(xmlpart):
956 requestedPrefixes = (
u'meta',
u'config',
u'dc',
u'style',
957 u'svg',
u'fo',
u'draw',
u'table',
u'form')
958 for prefix
in requestedPrefixes:
959 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))
986 mimetype = zipfd.read(
'mimetype').decode(
"utf-8")
991 manifestpart = zipfd.read(
'META-INF/manifest.xml')
993 for mentry,mvalue
in manifest.items():
995 assert(type(mvalue[
'media-type'])==type(
u""))
996 return mvalue[
'media-type']
999 return u'application/vnd.oasis.opendocument.text' 1009 z = zipfile.ZipFile(odffile)
1010 mimetype = __detectmimetype(z, odffile)
1014 manifestpart = z.read(
'META-INF/manifest.xml')
1016 __loadxmlparts(z, manifest, doc,
u'')
1017 for mentry,mvalue
in manifest.items():
1018 if mentry[:9] ==
u"Pictures/" and len(mentry) > 9:
1019 doc.addPicture(mvalue[
'full-path'], mvalue[
'media-type'], z.read(mentry))
1020 elif mentry ==
u"Thumbnails/thumbnail.png":
1021 doc.addThumbnail(z.read(mentry))
1022 elif mentry
in (
u'settings.xml',
u'meta.xml',
u'content.xml',
u'styles.xml'):
1025 elif mentry[:7] ==
u"Object " and len(mentry) < 11
and mentry[-1] ==
u"/":
1026 subdoc =
OpenDocument(mvalue[
'media-type'], add_generator=
False)
1027 doc.addObject(subdoc,
u"/" + mentry[:-1])
1028 __loadxmlparts(z, manifest, subdoc, mentry)
1029 elif mentry[:7] ==
u"Object ":
1032 if mvalue[
'full-path'][-1] ==
u'/':
1033 doc._extra.append(
OpaqueObject(mvalue[
'full-path'], mvalue[
'media-type'],
None))
1035 doc._extra.append(
OpaqueObject(mvalue[
'full-path'], mvalue[
'media-type'], z.read(mentry)))
1039 b = doc.getElementsByType(Body)
1040 if mimetype[:39] ==
u'application/vnd.oasis.opendocument.text':
1041 doc.text = b[0].firstChild
1042 elif mimetype[:43] ==
u'application/vnd.oasis.opendocument.graphics':
1043 doc.graphics = b[0].firstChild
1044 elif mimetype[:47] ==
u'application/vnd.oasis.opendocument.presentation':
1045 doc.presentation = b[0].firstChild
1046 elif mimetype[:46] ==
u'application/vnd.oasis.opendocument.spreadsheet':
1047 doc.spreadsheet = b[0].firstChild
1048 elif mimetype[:40] ==
u'application/vnd.oasis.opendocument.chart':
1049 doc.chart = b[0].firstChild
1050 elif mimetype[:40] ==
u'application/vnd.oasis.opendocument.image':
1051 doc.image = b[0].firstChild
1052 elif mimetype[:42] ==
u'application/vnd.oasis.opendocument.formula':
1053 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)