25__doc__=
"""Use OpenDocument to generate your documents."""
27import zipfile, time, uuid, sys, mimetypes, copy, os.path
30sys.path.append(os.path.dirname(__file__))
35from io
import StringIO, BytesIO
43from xml.sax.xmlreader
import InputSource
47if sys.version_info[0] == 3:
50__version__= TOOLSVERSION
55_XMLPROLOGUE =
u"<?xml version='1.0' encoding='UTF-8'?>\n"
70assert sys.version_info[0]>=2
and sys.version_info[1] >= 2
80 u'application/vnd.oasis.opendocument.text':
u'.odt',
81 u'application/vnd.oasis.opendocument.text-template':
u'.ott',
82 u'application/vnd.oasis.opendocument.graphics':
u'.odg',
83 u'application/vnd.oasis.opendocument.graphics-template':
u'.otg',
84 u'application/vnd.oasis.opendocument.presentation':
u'.odp',
85 u'application/vnd.oasis.opendocument.presentation-template':
u'.otp',
86 u'application/vnd.oasis.opendocument.spreadsheet':
u'.ods',
87 u'application/vnd.oasis.opendocument.spreadsheet-template':
u'.ots',
88 u'application/vnd.oasis.opendocument.chart':
u'.odc',
89 u'application/vnd.oasis.opendocument.chart-template':
u'.otc',
90 u'application/vnd.oasis.opendocument.image':
u'.odi',
91 u'application/vnd.oasis.opendocument.image-template':
u'.oti',
92 u'application/vnd.oasis.opendocument.formula':
u'.odf',
93 u'application/vnd.oasis.opendocument.formula-template':
u'.otf',
94 u'application/vnd.oasis.opendocument.text-master':
u'.odm',
95 u'application/vnd.oasis.opendocument.text-web':
u'.oth',
108 def __init__(self, filename, mediatype, content=None):
109 assert(type(filename)==type(
u""))
110 assert(type(mediatype)==type(
u""))
111 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
148 self.
meta.addElement(meta.Generator(text=TOOLSVERSION))
165 if node
is None: node = self.
topnode
167 for e
in node.childNodes:
168 if e.nodeType == element.Node.ELEMENT_NODE:
194 if elt.qname == (STYLENS,
u'style'):
196 styleref = elt.getAttrNS(TEXTNS,
u'style-name')
210 for e
in elt.childNodes:
211 if e.nodeType == element.Node.ELEMENT_NODE:
214 if elt.qname == (STYLENS,
u'style'):
225 def __register_stylename(self, elt):
228 name = elt.getAttrNS(STYLENS,
u'name')
231 if elt.parentNode.qname
in ((OFFICENS,
u'styles'), (OFFICENS,
u'automatic-styles')):
237 elt.setAttrNS(STYLENS,
u'name', name)
250 assert(type(filename)==type(
u""))
254 if sys.version_info[0]==2:
255 xml.write(_XMLPROLOGUE)
257 xml.write(_XMLPROLOGUE)
260 result=xml.getvalue()
262 f=codecs.open(filename,
'w', encoding=
'utf-8')
263 f.write(xml.getvalue())
274 if sys.version_info[0]==2:
275 xml.write(_XMLPROLOGUE)
277 xml.write(_XMLPROLOGUE)
279 return xml.getvalue().encode(
"utf-8")
288 xml.write(_XMLPROLOGUE)
290 x.write_open_tag(0, xml)
291 if self.
scripts.hasChildNodes():
297 if len(stylelist) > 0:
298 a.write_open_tag(1, xml)
301 a.write_close_tag(1, xml)
305 x.write_close_tag(0, xml)
306 return xml.getvalue().encode(
"utf-8")
315 def __manifestxml(self):
317 xml.write(_XMLPROLOGUE)
319 result=xml.getvalue()
320 assert(type(result)==type(
u""))
330 x.addElement(self.
meta)
332 xml.write(_XMLPROLOGUE)
334 result=xml.getvalue()
335 assert(type(result)==type(
u""))
346 if sys.version_info[0]==2:
347 xml.write(_XMLPROLOGUE)
349 xml.write(_XMLPROLOGUE)
351 result=xml.getvalue()
352 assert(type(result)==type(
u""))
364 for e
in top.childNodes:
365 if e.nodeType == element.Node.ELEMENT_NODE:
367 (CHARTNS,
u'style-name'),
368 (DRAWNS,
u'style-name'),
369 (DRAWNS,
u'text-style-name'),
370 (PRESENTATIONNS,
u'style-name'),
371 (STYLENS,
u'data-style-name'),
372 (STYLENS,
u'list-style-name'),
373 (STYLENS,
u'page-layout-name'),
374 (STYLENS,
u'style-name'),
375 (TABLENS,
u'default-cell-style-name'),
376 (TABLENS,
u'style-name'),
377 (TEXTNS,
u'style-name') ):
378 if e.getAttrNS(styleref[0],styleref[1]):
379 stylename = e.getAttrNS(styleref[0],styleref[1])
380 if stylename
not in stylenamelist:
383 stylenamelist.append(
unicode(stylename))
403 if isinstance(e,
element.Element)
and e.getAttrNS(STYLENS,
u'name')
in stylenamelist:
419 xml.write(_XMLPROLOGUE)
421 x.write_open_tag(0, xml)
426 a.write_open_tag(1, xml)
429 a.write_close_tag(1, xml)
432 x.write_close_tag(0, xml)
433 result = xml.getvalue()
435 assert(type(result)==type(
u""))
450 def addPicture(self, filename, mediatype=None, content=None):
452 if mediatype
is None:
453 mediatype, encoding = mimetypes.guess_type(filename)
454 if mediatype
is None:
456 try: ext = filename[filename.rindex(
u'.'):]
459 ext = mimetypes.guess_extension(mediatype)
460 manifestfn =
u"Pictures/%s%s" % (uuid.uuid4().hex.upper(), ext)
461 self.
Pictures[manifestfn] = (IS_FILENAME, filename, mediatype)
465 manifestfn = filename
466 self.
Pictures[manifestfn] = (IS_IMAGE, content, mediatype)
468 assert(type(filename)==type(
u""))
469 assert(type(content) == type(b
""))
484 if mediatype
is None:
485 mediatype, encoding = mimetypes.guess_type(filename)
486 if mediatype
is None:
488 try: ext = filename[filename.rindex(
u'.'):]
489 except ValueError: ext=
u''
491 ext = mimetypes.guess_extension(mediatype)
492 manifestfn =
u"Pictures/%s%s" % (uuid.uuid4().hex.upper(), ext)
493 self.
Pictures[manifestfn] = (IS_FILENAME, filename, mediatype)
495 assert(type(filename)==type(
u""))
496 assert(type(mediatype)==type(
u""))
511 assert(type(content)==type(b
""))
512 assert(type(mediatype)==type(
u""))
514 ext = mimetypes.guess_extension(mediatype)
515 manifestfn =
u"Pictures/%s%s" % (uuid.uuid4().hex.upper(), ext)
516 self.
Pictures[manifestfn] = (IS_IMAGE, content, mediatype)
525 assert(type(filecontent)==type(b
""))
527 if filecontent
is None:
541 assert(isinstance(document, OpenDocument))
542 assert(type(objectname)==type(
u"")
or objectname ==
None)
545 if objectname
is None:
548 document.folder = objectname
549 return u".%s" % document.folder
559 assert(isinstance(anObject, OpenDocument))
560 assert(type(folder)==type(
u""))
563 for arcname, picturerec
in anObject.Pictures.items():
564 what_it_is, fileobj, mediatype = picturerec
565 self.
manifest.addElement(manifest.FileEntry(fullpath=
u"%s%s" % ( folder ,arcname), mediatype=mediatype))
567 if what_it_is == IS_FILENAME:
568 self.
_z.
write(fileobj, folder + arcname, zipfile.ZIP_STORED)
570 zi = zipfile.ZipInfo(str(arcname), self.
_now)
571 zi.compress_type = zipfile.ZIP_STORED
572 zi.external_attr = UNIXPERMS
573 self.
_z.writestr(zi, fileobj)
579 for subobject
in anObject.childobjects:
580 self.
_savePictures(subobject,
u'%sObject %d/' % (folder, subobjectnum))
591 def __replaceGenerator(self):
592 for m
in self.
meta.childNodes[:]:
593 if hasattr(m,
'qname')
and m.qname == (METANS,
u'generator'):
594 self.
meta.removeChild(m)
595 self.
meta.addElement(meta.Generator(text=TOOLSVERSION))
605 def save(self, outputfile, addsuffix=False):
607 if outputfile ==
u'-':
608 outputfp = zipfile.ZipFile(sys.stdout,
"w")
611 outputfile = outputfile + odmimetypes.get(self.
mimetype,
u'.xxx')
612 outputfp = zipfile.ZipFile(outputfile,
"w")
622 zipoutputfp = zipfile.ZipFile(outputfp,
"w")
632 def __zipwrite(self, outputfp):
633 assert(isinstance(outputfp, zipfile.ZipFile))
636 self.
_now = time.localtime()[:6]
640 zi = zipfile.ZipInfo(
'mimetype', self.
_now)
641 zi.compress_type = zipfile.ZIP_STORED
642 zi.external_attr = UNIXPERMS
643 self.
_z.writestr(zi, self.
mimetype.encode(
"utf-8"))
652 self.
manifest.addElement(manifest.FileEntry(fullpath=
u"Thumbnails/", mediatype=
u''))
653 self.
manifest.addElement(manifest.FileEntry(fullpath=
u"Thumbnails/thumbnail.png", mediatype=
u''))
654 zi = zipfile.ZipInfo(
u"Thumbnails/thumbnail.png", self.
_now)
655 zi.compress_type = zipfile.ZIP_DEFLATED
656 zi.external_attr = UNIXPERMS
661 if op.filename ==
u"META-INF/documentsignatures.xml":
continue
662 self.
manifest.addElement(manifest.FileEntry(fullpath=op.filename, mediatype=op.mediatype))
663 if sys.version_info[0]==3:
664 zi = zipfile.ZipInfo(op.filename, self.
_now)
666 zi = zipfile.ZipInfo(op.filename.encode(
'utf-8'), self.
_now)
667 zi.compress_type = zipfile.ZIP_DEFLATED
668 zi.external_attr = UNIXPERMS
669 if op.content
is not None:
670 self.
_z.writestr(zi, op.content)
672 zi = zipfile.ZipInfo(
u"META-INF/manifest.xml", self.
_now)
673 zi.compress_type = zipfile.ZIP_DEFLATED
674 zi.external_attr = UNIXPERMS
689 assert(isinstance(anObject, OpenDocument))
690 assert(type(folder)==type(
u""))
693 self.
manifest.addElement(manifest.FileEntry(fullpath=
u"/", mediatype=anObject.mimetype))
695 self.
manifest.addElement(manifest.FileEntry(fullpath=folder, mediatype=anObject.mimetype))
697 self.
manifest.addElement(manifest.FileEntry(fullpath=
u"%sstyles.xml" % folder, mediatype=
u"text/xml"))
698 zi = zipfile.ZipInfo(
u"%sstyles.xml" % folder, self.
_now)
699 zi.compress_type = zipfile.ZIP_DEFLATED
700 zi.external_attr = UNIXPERMS
701 self.
_z.writestr(zi, anObject.stylesxml().encode(
"utf-8") )
704 self.
manifest.addElement(manifest.FileEntry(fullpath=
u"%scontent.xml" % folder, mediatype=
u"text/xml"))
705 zi = zipfile.ZipInfo(
u"%scontent.xml" % folder, self.
_now)
706 zi.compress_type = zipfile.ZIP_DEFLATED
707 zi.external_attr = UNIXPERMS
708 self.
_z.writestr(zi, anObject.contentxml() )
711 if anObject.settings.hasChildNodes():
712 self.
manifest.addElement(manifest.FileEntry(fullpath=
u"%ssettings.xml" % folder, mediatype=
u"text/xml"))
713 zi = zipfile.ZipInfo(
u"%ssettings.xml" % folder, self.
_now)
714 zi.compress_type = zipfile.ZIP_DEFLATED
715 zi.external_attr = UNIXPERMS
716 self.
_z.writestr(zi, anObject.settingsxml().encode(
"utf-8") )
720 self.
manifest.addElement(manifest.FileEntry(fullpath=
u"meta.xml", mediatype=
u"text/xml"))
721 zi = zipfile.ZipInfo(
u"meta.xml", self.
_now)
722 zi.compress_type = zipfile.ZIP_DEFLATED
723 zi.external_attr = UNIXPERMS
724 self.
_z.writestr(zi, anObject.metaxml().encode(
"utf-8") )
728 for subobject
in anObject.childobjects:
729 self.
_saveXmlObjects(subobject,
u'%sObject %d/' % (folder, subobjectnum))
746 return elt(check_grammar=
False)
754 assert(type(data)==type(
u""))
764 assert(type(data)==type(
u""))
773 assert (type(self.
mimetype)==type(
u""))
783 assert(type(name)==type(
u""))
785 ncname = make_NCName(name)
801 assert(isinstance (elt, types.FunctionType))
803 obj = elt(check_grammar=
False)
828 doc =
OpenDocument(
u'application/vnd.oasis.opendocument.chart')
830 doc.body.addElement(doc.chart)
838 doc =
OpenDocument(
u'application/vnd.oasis.opendocument.graphics')
840 doc.body.addElement(doc.drawing)
848 doc =
OpenDocument(
u'application/vnd.oasis.opendocument.image')
850 doc.body.addElement(doc.image)
858 doc =
OpenDocument(
u'application/vnd.oasis.opendocument.presentation')
860 doc.body.addElement(doc.presentation)
868 doc =
OpenDocument(
u'application/vnd.oasis.opendocument.spreadsheet')
870 doc.body.addElement(doc.spreadsheet)
878 doc =
OpenDocument(
u'application/vnd.oasis.opendocument.text')
880 doc.body.addElement(doc.text)
888 doc =
OpenDocument(
u'application/vnd.oasis.opendocument.text-master')
890 doc.body.addElement(doc.text)
902def __loadxmlparts(z, manifest, doc, objectpath):
903 assert(isinstance(z, zipfile.ZipFile))
904 assert(type(manifest)==type(dict()))
905 assert(isinstance(doc, OpenDocument))
906 assert(type(objectpath)==type(
u""))
909 from defusedxml.sax
import make_parser
910 from xml.sax
import handler
912 for xmlfile
in (objectpath+
u'settings.xml', objectpath+
u'meta.xml', objectpath+
u'content.xml', objectpath+
u'styles.xml'):
913 if xmlfile
not in manifest:
918 from xml.sax._exceptions
import SAXParseException
921 xmlpart = z.read(xmlfile).decode(
"utf-8")
922 doc._parsing = xmlfile
924 parser = make_parser()
925 parser.setFeature(handler.feature_namespaces, 1)
926 parser.setFeature(handler.feature_external_ges, 0)
927 parser.setContentHandler(LoadParser(doc))
928 parser.setErrorHandler(handler.ErrorHandler())
930 inpsrc = InputSource()
936 xmlpart=__fixXmlPart(xmlpart)
938 inpsrc.setByteStream(BytesIO(xmlpart.encode(
"utf-8")))
941 except KeyError
as v:
pass
942 except SAXParseException:
943 print (
u"====== SAX FAILED TO PARSE ==========\n", xmlpart)
954def __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:]
983def __detectmimetype(zipfd, odffile):
984 assert(isinstance(zipfd, zipfile.ZipFile))
987 mimetype = zipfd.read(
'mimetype').decode(
"utf-8")
992 manifestpart = zipfd.read(
'META-INF/manifest.xml')
993 manifest = manifestlist(manifestpart)
994 for mentry,mvalue
in manifest.items():
996 assert(type(mvalue[
'media-type'])==type(
u""))
997 return mvalue[
'media-type']
1000 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')
1015 manifest = manifestlist(manifestpart)
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
Creates a arbitrary element and is intended to be subclassed not used on its own.
just a record to bear a filename, a mediatype and a bytes content
__init__(self, filename, mediatype, content=None)
the constructor
createTextNode(self, data)
Method to create a text node.
getStyleByName(self, name)
Finds a style object based on the name.
metaxml(self)
Generates the meta.xml file.
clear_caches(self)
Clears internal caches.
stylesxml(self)
Generates the styles.xml file.
contentxml(self)
Generates the content.xml file.
addThumbnail(self, filecontent=None)
Add a fixed thumbnail The thumbnail in the library is big, so this is pretty useless.
_savePictures(self, anObject, folder)
saves pictures contained in an object
createElement(self, elt)
Inconvenient interface to create an element, but follows XML-DOM.
createCDATASection(self, data)
Method to create a CDATA section.
settingsxml(self)
Generates the settings.xml file.
toXml(self, filename=u'')
converts the document to a valid Xml format.
save(self, outputfile, addsuffix=False)
Save the document under the filename.
getElementsByType(self, elt)
Gets elements based on the type, which is function from text.py, draw.py etc.
_used_auto_styles(self, segments)
Loop through the masterstyles elements, and find the automatic styles that are used.
addPictureFromString(self, content, mediatype)
Add a picture from contents given as a Byte string.
_saveXmlObjects(self, anObject, folder)
save xml objects of an opendocument to some folder
_parseoneelement(self, top, stylenamelist)
Finds references to style objects in master-styles and add the style name to the style list if not al...
addObject(self, document, objectname=None)
Adds an object (subdocument).
remove_from_caches(self, elt)
Updates internal caches when an element has been removed.
__init__(self, mimetype, add_generator=True)
the constructor
build_caches(self, elt)
Builds internal caches; called from element.py.
rebuild_caches(self, node=None)
__register_stylename(self, elt)
write(self, outputfp)
User API to write the ODF file to an open file descriptor Writes the ZIP format.
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 ...
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 ...
getMediaType(self)
Returns the media type.
__zipwrite(self, outputfp)
Document(version="1.2", **args)
DocumentContent(version="1.2", **args)
DocumentStyles(version="1.2", **args)
DocumentMeta(version="1.2", **args)
DocumentSettings(version="1.2", **args)
OpenDocumentDrawing()
Creates a drawing document.
OpenDocumentText()
Creates a text document.
OpenDocumentPresentation()
Creates a presentation document.
OpenDocumentImage()
Creates an image document.
OpenDocumentSpreadsheet()
Creates a spreadsheet document.
OpenDocumentTextMaster()
Creates a text master document.
OpenDocumentChart()
Creates a chart document.