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
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
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:
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)
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)
265 return xml.getvalue().encode(
"utf-8")
275 xml.write(_XMLPROLOGUE)
277 x.write_open_tag(0, xml)
278 if self.
scripts.hasChildNodes():
284 if len(stylelist) > 0:
285 a.write_open_tag(1, xml)
288 a.write_close_tag(1, xml)
292 x.write_close_tag(0, xml)
293 return xml.getvalue().encode(
"utf-8")
301 def __manifestxml(self):
303 xml.write(_XMLPROLOGUE)
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):
387 if e.getAttrNS(STYLENS,
u'name')
in stylenamelist:
404 xml.write(_XMLPROLOGUE)
406 x.write_open_tag(0, xml)
411 a.write_open_tag(1, xml)
414 a.write_close_tag(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/%s%s" % (uuid.uuid4().hex.upper(), 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/%s%s" % (uuid.uuid4().hex.upper(), 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/%s%s" % (uuid.uuid4().hex.upper(), 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)
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):
596 if outputfile ==
u'-':
597 outputfp = zipfile.ZipFile(sys.stdout,
"w")
600 outputfile = outputfile + odmimetypes.get(self.
mimetype,
u'.xxx')
601 outputfp = zipfile.ZipFile(outputfile,
"w")
612 zipoutputfp = zipfile.ZipFile(outputfp,
"w")
621 def __zipwrite(self, outputfp):
622 assert(isinstance(outputfp, zipfile.ZipFile))
625 self.
_now = time.localtime()[:6]
629 zi = zipfile.ZipInfo(
'mimetype', self.
_now)
630 zi.compress_type = zipfile.ZIP_STORED
631 zi.external_attr = UNIXPERMS
632 self.
_z.writestr(zi, self.
mimetype.encode(
"utf-8"))
641 self.
manifest.addElement(manifest.FileEntry(fullpath=
u"Thumbnails/", mediatype=
u''))
642 self.
manifest.addElement(manifest.FileEntry(fullpath=
u"Thumbnails/thumbnail.png", mediatype=
u''))
643 zi = zipfile.ZipInfo(
u"Thumbnails/thumbnail.png", self.
_now)
644 zi.compress_type = zipfile.ZIP_DEFLATED
645 zi.external_attr = UNIXPERMS
650 if op.filename ==
u"META-INF/documentsignatures.xml":
continue 651 self.
manifest.addElement(manifest.FileEntry(fullpath=op.filename, mediatype=op.mediatype))
652 if sys.version_info[0]==3:
653 zi = zipfile.ZipInfo(op.filename, self.
_now)
655 zi = zipfile.ZipInfo(op.filename.encode(
'utf-8'), self.
_now)
656 zi.compress_type = zipfile.ZIP_DEFLATED
657 zi.external_attr = UNIXPERMS
658 if op.content
is not None:
659 self.
_z.writestr(zi, op.content)
661 zi = zipfile.ZipInfo(
u"META-INF/manifest.xml", self.
_now)
662 zi.compress_type = zipfile.ZIP_DEFLATED
663 zi.external_attr = UNIXPERMS
676 def _saveXmlObjects(self, anObject, folder):
677 assert(isinstance(anObject, OpenDocument))
678 assert(type(folder)==type(
u""))
681 self.
manifest.addElement(manifest.FileEntry(fullpath=
u"/", mediatype=anObject.mimetype))
683 self.
manifest.addElement(manifest.FileEntry(fullpath=folder, mediatype=anObject.mimetype))
685 self.
manifest.addElement(manifest.FileEntry(fullpath=
u"%sstyles.xml" % folder, mediatype=
u"text/xml"))
686 zi = zipfile.ZipInfo(
u"%sstyles.xml" % folder, self.
_now)
687 zi.compress_type = zipfile.ZIP_DEFLATED
688 zi.external_attr = UNIXPERMS
689 self.
_z.writestr(zi, anObject.stylesxml().encode(
"utf-8") )
692 self.
manifest.addElement(manifest.FileEntry(fullpath=
u"%scontent.xml" % folder, mediatype=
u"text/xml"))
693 zi = zipfile.ZipInfo(
u"%scontent.xml" % folder, self.
_now)
694 zi.compress_type = zipfile.ZIP_DEFLATED
695 zi.external_attr = UNIXPERMS
696 self.
_z.writestr(zi, anObject.contentxml() )
699 if anObject.settings.hasChildNodes():
700 self.
manifest.addElement(manifest.FileEntry(fullpath=
u"%ssettings.xml" % folder, mediatype=
u"text/xml"))
701 zi = zipfile.ZipInfo(
u"%ssettings.xml" % folder, self.
_now)
702 zi.compress_type = zipfile.ZIP_DEFLATED
703 zi.external_attr = UNIXPERMS
704 self.
_z.writestr(zi, anObject.settingsxml() )
708 self.
manifest.addElement(manifest.FileEntry(fullpath=
u"meta.xml", mediatype=
u"text/xml"))
709 zi = zipfile.ZipInfo(
u"meta.xml", self.
_now)
710 zi.compress_type = zipfile.ZIP_DEFLATED
711 zi.external_attr = UNIXPERMS
712 self.
_z.writestr(zi, anObject.metaxml() )
716 for subobject
in anObject.childobjects:
717 self.
_saveXmlObjects(subobject,
u'%sObject %d/' % (folder, subobjectnum))
735 return elt(check_grammar=
False)
744 assert(type(data)==type(
u""))
755 assert(type(data)==type(
u""))
765 assert (type(self.
mimetype)==type(
u""))
776 assert(type(name)==type(
u""))
795 assert(isinstance (elt, types.FunctionType))
797 obj = elt(check_grammar=
False)
823 doc =
OpenDocument(
u'application/vnd.oasis.opendocument.chart')
825 doc.body.addElement(doc.chart)
834 doc =
OpenDocument(
u'application/vnd.oasis.opendocument.graphics')
836 doc.body.addElement(doc.drawing)
845 doc =
OpenDocument(
u'application/vnd.oasis.opendocument.image')
847 doc.body.addElement(doc.image)
856 doc =
OpenDocument(
u'application/vnd.oasis.opendocument.presentation')
858 doc.body.addElement(doc.presentation)
867 doc =
OpenDocument(
u'application/vnd.oasis.opendocument.spreadsheet')
869 doc.body.addElement(doc.spreadsheet)
878 doc =
OpenDocument(
u'application/vnd.oasis.opendocument.text')
880 doc.body.addElement(doc.text)
889 doc =
OpenDocument(
u'application/vnd.oasis.opendocument.text-master')
891 doc.body.addElement(doc.text)
902 def __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""))
908 from load
import LoadParser
909 from xml.sax
import make_parser, handler
911 for xmlfile
in (objectpath+
u'settings.xml', objectpath+
u'meta.xml', objectpath+
u'content.xml', objectpath+
u'styles.xml'):
912 if xmlfile
not in manifest:
917 from xml.sax._exceptions
import SAXParseException
920 xmlpart = z.read(xmlfile).decode(
"utf-8")
921 doc._parsing = xmlfile
923 parser = make_parser()
924 parser.setFeature(handler.feature_namespaces, 1)
925 parser.setContentHandler(LoadParser(doc))
926 parser.setErrorHandler(handler.ErrorHandler())
928 inpsrc = InputSource()
934 xmlpart=__fixXmlPart(xmlpart)
936 inpsrc.setByteStream(BytesIO(xmlpart.encode(
"utf-8")))
939 except KeyError
as v:
pass 940 except SAXParseException:
941 print (
u"====== SAX FAILED TO PARSE ==========\n", xmlpart)
951 def __fixXmlPart(xmlpart):
953 requestedPrefixes = (
u'meta',
u'config',
u'dc',
u'style',
954 u'svg',
u'fo',
u'draw',
u'table',
u'form')
955 for prefix
in requestedPrefixes:
956 if u' xmlns:{prefix}'.format(prefix=prefix)
not in xmlpart:
964 pos=result.index(
u" xmlns:")
965 toInsert=
u' xmlns:{prefix}="urn:oasis:names:tc:opendocument:xmlns:{prefix}:1.0"'.format(prefix=prefix)
966 result=result[:pos]+toInsert+result[pos:]
979 def __detectmimetype(zipfd, odffile):
980 assert(isinstance(zipfd, zipfile.ZipFile))
983 mimetype = zipfd.read(
'mimetype').decode(
"utf-8")
988 manifestpart = zipfd.read(
'META-INF/manifest.xml')
990 for mentry,mvalue
in manifest.items():
992 assert(type(mvalue[
'media-type'])==type(
u""))
993 return mvalue[
'media-type']
996 return u'application/vnd.oasis.opendocument.text' 1006 z = zipfile.ZipFile(odffile)
1007 mimetype = __detectmimetype(z, odffile)
1011 manifestpart = z.read(
'META-INF/manifest.xml')
1013 __loadxmlparts(z, manifest, doc,
u'')
1014 for mentry,mvalue
in manifest.items():
1015 if mentry[:9] ==
u"Pictures/" and len(mentry) > 9:
1016 doc.addPicture(mvalue[
'full-path'], mvalue[
'media-type'], z.read(mentry))
1017 elif mentry ==
u"Thumbnails/thumbnail.png":
1018 doc.addThumbnail(z.read(mentry))
1019 elif mentry
in (
u'settings.xml',
u'meta.xml',
u'content.xml',
u'styles.xml'):
1022 elif mentry[:7] ==
u"Object " and len(mentry) < 11
and mentry[-1] ==
u"/":
1023 subdoc =
OpenDocument(mvalue[
'media-type'], add_generator=
False)
1024 doc.addObject(subdoc,
u"/" + mentry[:-1])
1025 __loadxmlparts(z, manifest, subdoc, mentry)
1026 elif mentry[:7] ==
u"Object ":
1029 if mvalue[
'full-path'][-1] ==
u'/':
1030 doc._extra.append(
OpaqueObject(mvalue[
'full-path'], mvalue[
'media-type'],
None))
1032 doc._extra.append(
OpaqueObject(mvalue[
'full-path'], mvalue[
'media-type'], z.read(mentry)))
1036 b = doc.getElementsByType(Body)
1037 if mimetype[:39] ==
u'application/vnd.oasis.opendocument.text':
1038 doc.text = b[0].firstChild
1039 elif mimetype[:43] ==
u'application/vnd.oasis.opendocument.graphics':
1040 doc.graphics = b[0].firstChild
1041 elif mimetype[:47] ==
u'application/vnd.oasis.opendocument.presentation':
1042 doc.presentation = b[0].firstChild
1043 elif mimetype[:46] ==
u'application/vnd.oasis.opendocument.spreadsheet':
1044 doc.spreadsheet = b[0].firstChild
1045 elif mimetype[:40] ==
u'application/vnd.oasis.opendocument.chart':
1046 doc.chart = b[0].firstChild
1047 elif mimetype[:40] ==
u'application/vnd.oasis.opendocument.image':
1048 doc.image = b[0].firstChild
1049 elif mimetype[:42] ==
u'application/vnd.oasis.opendocument.formula':
1050 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)