mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Add support for the Open Document Format (ODT) as an input format. Can now be converted to both LRF and EPUB. Fixes #233 (ODT2LRF request)
This commit is contained in:
parent
c1995474be
commit
323dfce595
@ -28,3 +28,4 @@ src/cssutils/_todo/
|
|||||||
src/cssutils/scripts/
|
src/cssutils/scripts/
|
||||||
src/cssutils/css/.svn/
|
src/cssutils/css/.svn/
|
||||||
src/cssutils/stylesheets/.svn/
|
src/cssutils/stylesheets/.svn/
|
||||||
|
src/odf/.svn
|
||||||
|
@ -21,4 +21,4 @@ class DRMError(ValueError):
|
|||||||
BOOK_EXTENSIONS = ['lrf', 'rar', 'zip', 'rtf', 'lit', 'txt', 'htm', 'xhtm',
|
BOOK_EXTENSIONS = ['lrf', 'rar', 'zip', 'rtf', 'lit', 'txt', 'htm', 'xhtm',
|
||||||
'html', 'xhtml', 'epub', 'pdf', 'prc', 'mobi', 'azw',
|
'html', 'xhtml', 'epub', 'pdf', 'prc', 'mobi', 'azw',
|
||||||
'epub', 'fb2', 'djvu', 'lrx', 'cbr', 'cbz', 'oebzip',
|
'epub', 'fb2', 'djvu', 'lrx', 'cbr', 'cbz', 'oebzip',
|
||||||
'rb', 'imp']
|
'rb', 'imp', 'odt']
|
||||||
|
@ -72,7 +72,6 @@ def pdf2opf(path, tdir, opts):
|
|||||||
def epub2opf(path, tdir, opts):
|
def epub2opf(path, tdir, opts):
|
||||||
zf = ZipFile(path)
|
zf = ZipFile(path)
|
||||||
zf.extractall(tdir)
|
zf.extractall(tdir)
|
||||||
print os.listdir(os.path.join(tdir, 'META-INF'))
|
|
||||||
if os.path.exists(os.path.join(tdir, 'META-INF', 'encryption.xml')):
|
if os.path.exists(os.path.join(tdir, 'META-INF', 'encryption.xml')):
|
||||||
raise DRMError(os.path.basename(path))
|
raise DRMError(os.path.basename(path))
|
||||||
for f in walk(tdir):
|
for f in walk(tdir):
|
||||||
@ -80,6 +79,10 @@ def epub2opf(path, tdir, opts):
|
|||||||
return f
|
return f
|
||||||
raise ValueError('%s is not a valid EPUB file'%path)
|
raise ValueError('%s is not a valid EPUB file'%path)
|
||||||
|
|
||||||
|
def odt2epub(path, tdir, opts):
|
||||||
|
from calibre.ebooks.odt.to_oeb import Extract
|
||||||
|
opts.encoding = 'utf-8'
|
||||||
|
return Extract()(path, tdir)
|
||||||
|
|
||||||
MAP = {
|
MAP = {
|
||||||
'lit' : lit2opf,
|
'lit' : lit2opf,
|
||||||
@ -90,8 +93,9 @@ MAP = {
|
|||||||
'txt' : txt2opf,
|
'txt' : txt2opf,
|
||||||
'pdf' : pdf2opf,
|
'pdf' : pdf2opf,
|
||||||
'epub' : epub2opf,
|
'epub' : epub2opf,
|
||||||
|
'odt' : odt2epub,
|
||||||
}
|
}
|
||||||
SOURCE_FORMATS = ['lit', 'mobi', 'prc', 'fb2', 'rtf', 'txt', 'pdf', 'rar', 'zip', 'oebzip', 'htm', 'html', 'epub']
|
SOURCE_FORMATS = ['lit', 'mobi', 'prc', 'fb2', 'odt', 'rtf', 'txt', 'pdf', 'rar', 'zip', 'oebzip', 'htm', 'html', 'epub']
|
||||||
|
|
||||||
def unarchive(path, tdir):
|
def unarchive(path, tdir):
|
||||||
extract(path, tdir)
|
extract(path, tdir)
|
||||||
|
@ -23,6 +23,7 @@ preferred_source_formats = [
|
|||||||
'LIT',
|
'LIT',
|
||||||
'MOBI',
|
'MOBI',
|
||||||
'EPUB',
|
'EPUB',
|
||||||
|
'ODT',
|
||||||
'HTML',
|
'HTML',
|
||||||
'HTM',
|
'HTM',
|
||||||
'XHTM',
|
'XHTM',
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
from __future__ import with_statement
|
||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
'''Convert any ebook file into a LRF file.'''
|
'''Convert any ebook file into a LRF file.'''
|
||||||
@ -7,6 +8,7 @@ import sys, os, logging, shutil, tempfile, re
|
|||||||
from calibre.ebooks import UnknownFormatError
|
from calibre.ebooks import UnknownFormatError
|
||||||
from calibre.ebooks.lrf import option_parser as _option_parser
|
from calibre.ebooks.lrf import option_parser as _option_parser
|
||||||
from calibre import __appname__, setup_cli_handlers, extract
|
from calibre import __appname__, setup_cli_handlers, extract
|
||||||
|
from calibre.ptempfile import TemporaryDirectory
|
||||||
from calibre.ebooks.lrf.lit.convert_from import process_file as lit2lrf
|
from calibre.ebooks.lrf.lit.convert_from import process_file as lit2lrf
|
||||||
from calibre.ebooks.lrf.pdf.convert_from import process_file as pdf2lrf
|
from calibre.ebooks.lrf.pdf.convert_from import process_file as pdf2lrf
|
||||||
from calibre.ebooks.lrf.rtf.convert_from import process_file as rtf2lrf
|
from calibre.ebooks.lrf.rtf.convert_from import process_file as rtf2lrf
|
||||||
@ -89,6 +91,21 @@ def handle_archive(path):
|
|||||||
file = file.decode(sys.getfilesystemencoding())
|
file = file.decode(sys.getfilesystemencoding())
|
||||||
return tdir, file
|
return tdir, file
|
||||||
|
|
||||||
|
def odt2lrf(path, options, logger):
|
||||||
|
from calibre.ebooks.odt.to_oeb import Extract
|
||||||
|
from calibre.ebooks.lrf.html.convert_from import process_file as html_process_file
|
||||||
|
|
||||||
|
if logger is None:
|
||||||
|
level = logging.DEBUG if options.verbose else logging.INFO
|
||||||
|
logger = logging.getLogger('odt2lrf')
|
||||||
|
setup_cli_handlers(logger, level)
|
||||||
|
|
||||||
|
with TemporaryDirectory('_odt2lrf') as tdir:
|
||||||
|
opf = Extract()(path, tdir)
|
||||||
|
options.use_spine = True
|
||||||
|
options.encoding = 'utf-8'
|
||||||
|
html_process_file(opf.replace('metadata.opf', 'index.html'), options, logger)
|
||||||
|
|
||||||
def process_file(path, options, logger=None):
|
def process_file(path, options, logger=None):
|
||||||
path = os.path.abspath(os.path.expanduser(path))
|
path = os.path.abspath(os.path.expanduser(path))
|
||||||
tdir = None
|
tdir = None
|
||||||
@ -138,8 +155,10 @@ def process_file(path, options, logger=None):
|
|||||||
convertor = mobi2lrf
|
convertor = mobi2lrf
|
||||||
elif ext == 'fb2':
|
elif ext == 'fb2':
|
||||||
convertor = fb22lrf
|
convertor = fb22lrf
|
||||||
|
elif ext == 'odt':
|
||||||
|
convertor = odt2lrf
|
||||||
if not convertor:
|
if not convertor:
|
||||||
raise UnknownFormatError('Coverting from %s to LRF is not supported.'%ext)
|
raise UnknownFormatError(_('Converting from %s to LRF is not supported.')%ext)
|
||||||
convertor(path, options, logger)
|
convertor(path, options, logger)
|
||||||
finally:
|
finally:
|
||||||
os.chdir(cwd)
|
os.chdir(cwd)
|
||||||
|
@ -14,17 +14,17 @@ from calibre.ebooks.metadata.rb import get_metadata as rb_metadata
|
|||||||
from calibre.ebooks.metadata.epub import get_metadata as epub_metadata
|
from calibre.ebooks.metadata.epub import get_metadata as epub_metadata
|
||||||
from calibre.ebooks.metadata.html import get_metadata as html_metadata
|
from calibre.ebooks.metadata.html import get_metadata as html_metadata
|
||||||
from calibre.ebooks.mobi.reader import get_metadata as mobi_metadata
|
from calibre.ebooks.mobi.reader import get_metadata as mobi_metadata
|
||||||
|
from calibre.ebooks.metadata.odt import get_metadata as odt_metadata
|
||||||
from calibre.ebooks.metadata.opf import OPFReader
|
from calibre.ebooks.metadata.opf import OPFReader
|
||||||
from calibre.ebooks.metadata.rtf import set_metadata as set_rtf_metadata
|
from calibre.ebooks.metadata.rtf import set_metadata as set_rtf_metadata
|
||||||
from calibre.ebooks.lrf.meta import set_metadata as set_lrf_metadata
|
from calibre.ebooks.lrf.meta import set_metadata as set_lrf_metadata
|
||||||
from calibre.ebooks.metadata.epub import set_metadata as set_epub_metadata
|
from calibre.ebooks.metadata.epub import set_metadata as set_epub_metadata
|
||||||
|
|
||||||
from calibre.ebooks.metadata import MetaInformation
|
from calibre.ebooks.metadata import MetaInformation
|
||||||
from calibre.utils.config import prefs
|
|
||||||
|
|
||||||
_METADATA_PRIORITIES = [
|
_METADATA_PRIORITIES = [
|
||||||
'html', 'htm', 'xhtml', 'xhtm',
|
'html', 'htm', 'xhtml', 'xhtm',
|
||||||
'rtf', 'fb2', 'pdf', 'prc',
|
'rtf', 'fb2', 'pdf', 'prc', 'odt',
|
||||||
'epub', 'lit', 'lrf', 'mobi', 'rb', 'imp'
|
'epub', 'lit', 'lrf', 'mobi', 'rb', 'imp'
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -64,6 +64,8 @@ def get_metadata(stream, stream_type='lrf', use_libprs_metadata=False):
|
|||||||
stream_type = 'html'
|
stream_type = 'html'
|
||||||
if stream_type in ('mobi', 'prc'):
|
if stream_type in ('mobi', 'prc'):
|
||||||
stream_type = 'mobi'
|
stream_type = 'mobi'
|
||||||
|
if stream_type in ('odt', 'ods', 'odp', 'odg', 'odf'):
|
||||||
|
stream_type = 'odt'
|
||||||
|
|
||||||
opf = None
|
opf = None
|
||||||
if hasattr(stream, 'name'):
|
if hasattr(stream, 'name'):
|
||||||
|
266
src/calibre/ebooks/metadata/odt.py
Executable file
266
src/calibre/ebooks/metadata/odt.py
Executable file
@ -0,0 +1,266 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2006 Søren Roug, European Environment Agency
|
||||||
|
#
|
||||||
|
# This is free software. You may redistribute it under the terms
|
||||||
|
# of the Apache license and the GNU General Public License Version
|
||||||
|
# 2 or at your option any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public
|
||||||
|
# License along with this program; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
#
|
||||||
|
# Contributor(s):
|
||||||
|
#
|
||||||
|
import zipfile, sys, re
|
||||||
|
import xml.sax.saxutils
|
||||||
|
from cStringIO import StringIO
|
||||||
|
|
||||||
|
from odf.namespaces import OFFICENS, DCNS, METANS
|
||||||
|
from calibre.ebooks.metadata import MetaInformation, string_to_authors
|
||||||
|
|
||||||
|
whitespace = re.compile(r'\s+')
|
||||||
|
|
||||||
|
fields = {
|
||||||
|
'title': (DCNS,u'title'),
|
||||||
|
'description': (DCNS,u'description'),
|
||||||
|
'subject': (DCNS,u'subject'),
|
||||||
|
'creator': (DCNS,u'creator'),
|
||||||
|
'date': (DCNS,u'date'),
|
||||||
|
'language': (DCNS,u'language'),
|
||||||
|
'generator': (METANS,u'generator'),
|
||||||
|
'initial-creator': (METANS,u'initial-creator'),
|
||||||
|
'keyword': (METANS,u'keyword'),
|
||||||
|
'editing-duration': (METANS,u'editing-duration'),
|
||||||
|
'editing-cycles': (METANS,u'editing-cycles'),
|
||||||
|
'printed-by': (METANS,u'printed-by'),
|
||||||
|
'print-date': (METANS,u'print-date'),
|
||||||
|
'creation-date': (METANS,u'creation-date'),
|
||||||
|
'user-defined': (METANS,u'user-defined'),
|
||||||
|
#'template': (METANS,u'template'),
|
||||||
|
}
|
||||||
|
|
||||||
|
def normalize(str):
|
||||||
|
"""
|
||||||
|
The normalize-space function returns the argument string with whitespace
|
||||||
|
normalized by stripping leading and trailing whitespace and replacing
|
||||||
|
sequences of whitespace characters by a single space.
|
||||||
|
"""
|
||||||
|
return whitespace.sub(' ', str).strip()
|
||||||
|
|
||||||
|
class MetaCollector:
|
||||||
|
"""
|
||||||
|
The MetaCollector is a pseudo file object, that can temporarily ignore write-calls
|
||||||
|
It could probably be replaced with a StringIO object.
|
||||||
|
"""
|
||||||
|
def __init__(self):
|
||||||
|
self._content = []
|
||||||
|
self.dowrite = True
|
||||||
|
|
||||||
|
def write(self, str):
|
||||||
|
if self.dowrite:
|
||||||
|
self._content.append(str)
|
||||||
|
|
||||||
|
def content(self):
|
||||||
|
return ''.join(self._content)
|
||||||
|
|
||||||
|
|
||||||
|
class odfmetaparser(xml.sax.saxutils.XMLGenerator):
|
||||||
|
""" Parse a meta.xml file with an event-driven parser and replace elements.
|
||||||
|
It would probably be a cleaner approach to use a DOM based parser and
|
||||||
|
then manipulate in memory.
|
||||||
|
Small issue: Reorders elements
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, deletefields={}, yieldfields={}, addfields={}):
|
||||||
|
self.deletefields = deletefields
|
||||||
|
self.yieldfields = yieldfields
|
||||||
|
self.addfields = addfields
|
||||||
|
self._mimetype = ''
|
||||||
|
self.output = MetaCollector()
|
||||||
|
self._data = []
|
||||||
|
self.seenfields = {}
|
||||||
|
xml.sax.saxutils.XMLGenerator.__init__(self, self.output, 'utf-8')
|
||||||
|
|
||||||
|
def startElementNS(self, name, qname, attrs):
|
||||||
|
self._data = []
|
||||||
|
field = name
|
||||||
|
# I can't modify the template until the tool replaces elements at the same
|
||||||
|
# location and not at the end
|
||||||
|
# if name == (METANS,u'template'):
|
||||||
|
# self._data = [attrs.get((XLINKNS,u'title'),'')]
|
||||||
|
if name == (METANS,u'user-defined'):
|
||||||
|
field = attrs.get((METANS,u'name'))
|
||||||
|
if field in self.deletefields:
|
||||||
|
self.output.dowrite = False
|
||||||
|
elif field in self.yieldfields:
|
||||||
|
del self.addfields[field]
|
||||||
|
xml.sax.saxutils.XMLGenerator.startElementNS(self, name, qname, attrs)
|
||||||
|
else:
|
||||||
|
xml.sax.saxutils.XMLGenerator.startElementNS(self, name, qname, attrs)
|
||||||
|
self._tag = field
|
||||||
|
|
||||||
|
def endElementNS(self, name, qname):
|
||||||
|
field = name
|
||||||
|
if name == (METANS,u'user-defined'):
|
||||||
|
field = self._tag
|
||||||
|
if name == (OFFICENS,u'meta'):
|
||||||
|
for k,v in self.addfields.items():
|
||||||
|
if len(v) > 0:
|
||||||
|
if type(k) == type(''):
|
||||||
|
xml.sax.saxutils.XMLGenerator.startElementNS(self,(METANS,u'user-defined'),None,{(METANS,u'name'):k})
|
||||||
|
xml.sax.saxutils.XMLGenerator.characters(self, v)
|
||||||
|
xml.sax.saxutils.XMLGenerator.endElementNS(self, (METANS,u'user-defined'),None)
|
||||||
|
else:
|
||||||
|
xml.sax.saxutils.XMLGenerator.startElementNS(self, k, None, {})
|
||||||
|
xml.sax.saxutils.XMLGenerator.characters(self, v)
|
||||||
|
xml.sax.saxutils.XMLGenerator.endElementNS(self, k, None)
|
||||||
|
if isinstance(self._tag, tuple):
|
||||||
|
texttag = self._tag[1]
|
||||||
|
else:
|
||||||
|
texttag = self._tag
|
||||||
|
self.seenfields[texttag] = self.data()
|
||||||
|
|
||||||
|
if field in self.deletefields:
|
||||||
|
self.output.dowrite = True
|
||||||
|
else:
|
||||||
|
xml.sax.saxutils.XMLGenerator.endElementNS(self, name, qname)
|
||||||
|
|
||||||
|
def characters(self, content):
|
||||||
|
xml.sax.saxutils.XMLGenerator.characters(self, content)
|
||||||
|
self._data.append(content)
|
||||||
|
|
||||||
|
def meta(self):
|
||||||
|
return self.output.content()
|
||||||
|
|
||||||
|
def data(self):
|
||||||
|
return normalize(''.join(self._data))
|
||||||
|
|
||||||
|
def get_metadata(stream):
|
||||||
|
zin = zipfile.ZipFile(stream, 'r')
|
||||||
|
odfs = odfmetaparser()
|
||||||
|
parser = xml.sax.make_parser()
|
||||||
|
parser.setFeature(xml.sax.handler.feature_namespaces, 1)
|
||||||
|
parser.setContentHandler(odfs)
|
||||||
|
content = zin.read('meta.xml')
|
||||||
|
parser.parse(StringIO(content))
|
||||||
|
data = odfs.seenfields
|
||||||
|
mi = MetaInformation(None, [])
|
||||||
|
if data.has_key('title'):
|
||||||
|
mi.title = data['title']
|
||||||
|
if data.has_key('creator'):
|
||||||
|
mi.authors = string_to_authors(data['creator'])
|
||||||
|
if data.has_key('description'):
|
||||||
|
mi.comments = data['description']
|
||||||
|
if data.has_key('language'):
|
||||||
|
mi.language = data['language']
|
||||||
|
if data.get('keywords', ''):
|
||||||
|
mi.tags = data['keywords'].split(',')
|
||||||
|
|
||||||
|
return mi
|
||||||
|
|
||||||
|
def main(args=sys.argv):
|
||||||
|
if len(args) != 2:
|
||||||
|
print 'Usage: %s file.odt'%args[0]
|
||||||
|
return 1
|
||||||
|
mi = get_metadata(open(args[1], 'rb'))
|
||||||
|
print mi
|
||||||
|
return 0
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.exit(main())
|
||||||
|
|
||||||
|
#now = time.localtime()[:6]
|
||||||
|
#outputfile = "-"
|
||||||
|
#writemeta = False # Do we change any meta data?
|
||||||
|
#usenormalize = False
|
||||||
|
#
|
||||||
|
#try:
|
||||||
|
# opts, args = getopt.getopt(sys.argv[1:], "cdlI:A:a:o:x:X:")
|
||||||
|
#except getopt.GetoptError:
|
||||||
|
# exitwithusage()
|
||||||
|
#
|
||||||
|
#if len(opts) == 0:
|
||||||
|
# opts = [ ('-l','') ]
|
||||||
|
#
|
||||||
|
#for o, a in opts:
|
||||||
|
# if o in ('-a','-A','-I'):
|
||||||
|
# writemeta = True
|
||||||
|
# if a.find(":") >= 0:
|
||||||
|
# k,v = a.split(":",1)
|
||||||
|
# else:
|
||||||
|
# k,v = (a, "")
|
||||||
|
# if len(k) == 0:
|
||||||
|
# exitwithusage()
|
||||||
|
# k = fields.get(k,k)
|
||||||
|
# addfields[k] = unicode(v,'utf-8')
|
||||||
|
# if o == '-a':
|
||||||
|
# yieldfields[k] = True
|
||||||
|
# if o == '-I':
|
||||||
|
# deletefields[k] = True
|
||||||
|
# if o == '-d':
|
||||||
|
# writemeta = True
|
||||||
|
# addfields[(DCNS,u'date')] = "%04d-%02d-%02dT%02d:%02d:%02d" % now
|
||||||
|
# deletefields[(DCNS,u'date')] = True
|
||||||
|
# if o == '-c':
|
||||||
|
# usenormalize = True
|
||||||
|
# if o == '-l':
|
||||||
|
# Xfields = fields.values()
|
||||||
|
# if o == "-x":
|
||||||
|
# xfields.append(fields.get(a,a))
|
||||||
|
# if o == "-X":
|
||||||
|
# Xfields.append(fields.get(a,a))
|
||||||
|
# if o == "-o":
|
||||||
|
# outputfile = a
|
||||||
|
#
|
||||||
|
## The specification says we should change the element to our own,
|
||||||
|
## and must not export the original identifier.
|
||||||
|
#if writemeta:
|
||||||
|
# addfields[(METANS,u'generator')] = TOOLSVERSION
|
||||||
|
# deletefields[(METANS,u'generator')] = True
|
||||||
|
#
|
||||||
|
#odfs = odfmetaparser()
|
||||||
|
#parser = xml.sax.make_parser()
|
||||||
|
#parser.setFeature(xml.sax.handler.feature_namespaces, 1)
|
||||||
|
#parser.setContentHandler(odfs)
|
||||||
|
#
|
||||||
|
#if len(args) == 0:
|
||||||
|
# zin = zipfile.ZipFile(sys.stdin,'r')
|
||||||
|
#else:
|
||||||
|
# if not zipfile.is_zipfile(args[0]):
|
||||||
|
# exitwithusage()
|
||||||
|
# zin = zipfile.ZipFile(args[0], 'r')
|
||||||
|
#
|
||||||
|
#content = zin.read('meta.xml')
|
||||||
|
#parser.parse(StringIO(content))
|
||||||
|
#
|
||||||
|
#if writemeta:
|
||||||
|
# if outputfile == '-':
|
||||||
|
# if sys.stdout.isatty():
|
||||||
|
# sys.stderr.write("Won't write ODF file to terminal\n")
|
||||||
|
# sys.exit(1)
|
||||||
|
# zout = zipfile.ZipFile(sys.stdout,"w")
|
||||||
|
# else:
|
||||||
|
# zout = zipfile.ZipFile(outputfile,"w")
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# # Loop through the input zipfile and copy the content to the output until we
|
||||||
|
# # get to the meta.xml. Then substitute.
|
||||||
|
# for zinfo in zin.infolist():
|
||||||
|
# if zinfo.filename == "meta.xml":
|
||||||
|
# # Write meta
|
||||||
|
# zi = zipfile.ZipInfo("meta.xml", now)
|
||||||
|
# zi.compress_type = zipfile.ZIP_DEFLATED
|
||||||
|
# zout.writestr(zi,odfs.meta() )
|
||||||
|
# else:
|
||||||
|
# payload = zin.read(zinfo.filename)
|
||||||
|
# zout.writestr(zinfo, payload)
|
||||||
|
#
|
||||||
|
# zout.close()
|
||||||
|
#zin.close()
|
9
src/calibre/ebooks/odt/__init__.py
Normal file
9
src/calibre/ebooks/odt/__init__.py
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
||||||
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
|
'''
|
||||||
|
Handle the Open Document Format
|
||||||
|
'''
|
||||||
|
|
72
src/calibre/ebooks/odt/to_oeb.py
Normal file
72
src/calibre/ebooks/odt/to_oeb.py
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
from __future__ import with_statement
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
||||||
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
|
'''
|
||||||
|
Convert an ODT file into a Open Ebook
|
||||||
|
'''
|
||||||
|
import os, sys
|
||||||
|
from odf.odf2xhtml import ODF2XHTML
|
||||||
|
|
||||||
|
from calibre import CurrentDir, walk
|
||||||
|
from calibre.utils.zipfile import ZipFile
|
||||||
|
from calibre.utils.config import OptionParser
|
||||||
|
from calibre.ebooks.metadata.odt import get_metadata
|
||||||
|
from calibre.ebooks.metadata.opf2 import OPFCreator
|
||||||
|
|
||||||
|
class Extract(ODF2XHTML):
|
||||||
|
|
||||||
|
def extract_pictures(self, zf):
|
||||||
|
if not os.path.exists('Pictures'):
|
||||||
|
os.makedirs('Pictures')
|
||||||
|
for name in zf.namelist():
|
||||||
|
if name.startswith('Pictures'):
|
||||||
|
data = zf.read(name)
|
||||||
|
with open(name, 'wb') as f:
|
||||||
|
f.write(data)
|
||||||
|
|
||||||
|
def __call__(self, path, odir):
|
||||||
|
if not os.path.exists(odir):
|
||||||
|
os.makedirs(odir)
|
||||||
|
path = os.path.abspath(path)
|
||||||
|
with CurrentDir(odir):
|
||||||
|
print 'Extracting ODT file...'
|
||||||
|
html = self.odf2xhtml(path)
|
||||||
|
with open('index.html', 'wb') as f:
|
||||||
|
f.write(html.encode('utf-8'))
|
||||||
|
with open(path, 'rb') as f:
|
||||||
|
zf = ZipFile(f, 'r')
|
||||||
|
self.extract_pictures(zf)
|
||||||
|
f.seek(0)
|
||||||
|
mi = get_metadata(f)
|
||||||
|
if not mi.title:
|
||||||
|
mi.title = os.path.splitext(os.path.basename(path))
|
||||||
|
if not mi.authors:
|
||||||
|
mi.authors = [_('Unknown')]
|
||||||
|
opf = OPFCreator(os.path.abspath(os.getcwdu()), mi)
|
||||||
|
opf.create_manifest([(os.path.abspath(f), None) for f in walk(os.getcwd())])
|
||||||
|
opf.create_spine([os.path.abspath('index.html')])
|
||||||
|
with open('metadata.opf', 'wb') as f:
|
||||||
|
opf.render(f)
|
||||||
|
return os.path.abspath('metadata.opf')
|
||||||
|
|
||||||
|
def option_parser():
|
||||||
|
parser = OptionParser('%prog [options] file.odt')
|
||||||
|
parser.add_option('-o', '--output-dir', default='.',
|
||||||
|
help=_('The output directory. Defaults to the current directory.'))
|
||||||
|
return parser
|
||||||
|
|
||||||
|
def main(args=sys.argv):
|
||||||
|
parser = option_parser()
|
||||||
|
opts, args = parser.parse_args(args)
|
||||||
|
if len(args) < 2:
|
||||||
|
parser.print_help()
|
||||||
|
print 'No ODT file specified'
|
||||||
|
return 1
|
||||||
|
Extract()(args[1], os.path.abspath(opts.output_dir))
|
||||||
|
print 'Extracted to', os.path.abspath(opts.output_dir)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.exit(main())
|
@ -24,11 +24,13 @@ entry_points = {
|
|||||||
'imp-meta = calibre.ebooks.metadata.imp:main',
|
'imp-meta = calibre.ebooks.metadata.imp:main',
|
||||||
'rb-meta = calibre.ebooks.metadata.rb:main',
|
'rb-meta = calibre.ebooks.metadata.rb:main',
|
||||||
'opf-meta = calibre.ebooks.metadata.opf:main',
|
'opf-meta = calibre.ebooks.metadata.opf:main',
|
||||||
|
'odt-meta = calibre.ebooks.metadata.odt:main',
|
||||||
'epub-meta = calibre.ebooks.metadata.epub:main',
|
'epub-meta = calibre.ebooks.metadata.epub:main',
|
||||||
'txt2lrf = calibre.ebooks.lrf.txt.convert_from:main',
|
'txt2lrf = calibre.ebooks.lrf.txt.convert_from:main',
|
||||||
'html2lrf = calibre.ebooks.lrf.html.convert_from:main',
|
'html2lrf = calibre.ebooks.lrf.html.convert_from:main',
|
||||||
'html2oeb = calibre.ebooks.html:main',
|
'html2oeb = calibre.ebooks.html:main',
|
||||||
'html2epub = calibre.ebooks.epub.from_html:main',
|
'html2epub = calibre.ebooks.epub.from_html:main',
|
||||||
|
'odt2oeb = calibre.ebooks.odt.to_oeb:main',
|
||||||
'markdown-calibre = calibre.ebooks.markdown.markdown:main',
|
'markdown-calibre = calibre.ebooks.markdown.markdown:main',
|
||||||
'lit2lrf = calibre.ebooks.lrf.lit.convert_from:main',
|
'lit2lrf = calibre.ebooks.lrf.lit.convert_from:main',
|
||||||
'epub2lrf = calibre.ebooks.lrf.epub.convert_from:main',
|
'epub2lrf = calibre.ebooks.lrf.epub.convert_from:main',
|
||||||
@ -176,11 +178,12 @@ def setup_completion(fatal_errors):
|
|||||||
from calibre.ebooks.lrf.comic.convert_from import option_parser as comicop
|
from calibre.ebooks.lrf.comic.convert_from import option_parser as comicop
|
||||||
from calibre.ebooks.epub.from_html import option_parser as html2epub
|
from calibre.ebooks.epub.from_html import option_parser as html2epub
|
||||||
from calibre.ebooks.html import option_parser as html2oeb
|
from calibre.ebooks.html import option_parser as html2oeb
|
||||||
|
from calibre.ebooks.odt.to_oeb import option_parser as odt2oeb
|
||||||
from calibre.ebooks.epub.from_feeds import option_parser as feeds2epub
|
from calibre.ebooks.epub.from_feeds import option_parser as feeds2epub
|
||||||
from calibre.ebooks.epub.from_any import option_parser as any2epub
|
from calibre.ebooks.epub.from_any import option_parser as any2epub
|
||||||
from calibre.ebooks.epub.from_comic import option_parser as comic2epub
|
from calibre.ebooks.epub.from_comic import option_parser as comic2epub
|
||||||
any_formats = ['epub', 'htm', 'html', 'xhtml', 'xhtm', 'rar', 'zip',
|
any_formats = ['epub', 'htm', 'html', 'xhtml', 'xhtm', 'rar', 'zip',
|
||||||
'txt', 'lit', 'rtf', 'pdf', 'prc', 'mobi', 'fb2']
|
'txt', 'lit', 'rtf', 'pdf', 'prc', 'mobi', 'fb2', 'odt']
|
||||||
f = open_file('/etc/bash_completion.d/libprs500')
|
f = open_file('/etc/bash_completion.d/libprs500')
|
||||||
f.close()
|
f.close()
|
||||||
os.remove(f.name)
|
os.remove(f.name)
|
||||||
@ -208,6 +211,7 @@ def setup_completion(fatal_errors):
|
|||||||
f.write(opts_and_exts('imp-meta', metaop, ['imp']))
|
f.write(opts_and_exts('imp-meta', metaop, ['imp']))
|
||||||
f.write(opts_and_exts('rb-meta', metaop, ['rb']))
|
f.write(opts_and_exts('rb-meta', metaop, ['rb']))
|
||||||
f.write(opts_and_exts('opf-meta', metaop, ['opf']))
|
f.write(opts_and_exts('opf-meta', metaop, ['opf']))
|
||||||
|
f.write(opts_and_exts('odt-meta', metaop, ['odt', 'ods', 'odf', 'odg', 'odp']))
|
||||||
f.write(opts_and_exts('epub-meta', epub_meta, ['epub']))
|
f.write(opts_and_exts('epub-meta', epub_meta, ['epub']))
|
||||||
f.write(opts_and_exts('lrfviewer', lrfviewerop, ['lrf']))
|
f.write(opts_and_exts('lrfviewer', lrfviewerop, ['lrf']))
|
||||||
f.write(opts_and_exts('pdfrelow', pdfhtmlop, ['pdf']))
|
f.write(opts_and_exts('pdfrelow', pdfhtmlop, ['pdf']))
|
||||||
@ -220,6 +224,7 @@ def setup_completion(fatal_errors):
|
|||||||
f.write(opts_and_words('feeds2lrf', feeds2epub, feed_titles))
|
f.write(opts_and_words('feeds2lrf', feeds2epub, feed_titles))
|
||||||
f.write(opts_and_exts('html2epub', html2epub, ['html', 'htm', 'xhtm', 'xhtml', 'opf']))
|
f.write(opts_and_exts('html2epub', html2epub, ['html', 'htm', 'xhtm', 'xhtml', 'opf']))
|
||||||
f.write(opts_and_exts('html2oeb', html2oeb, ['html', 'htm', 'xhtm', 'xhtml']))
|
f.write(opts_and_exts('html2oeb', html2oeb, ['html', 'htm', 'xhtm', 'xhtml']))
|
||||||
|
f.write(opts_and_exts('odt2oeb', odt2oeb, ['odt']))
|
||||||
f.write('''
|
f.write('''
|
||||||
_prs500_ls()
|
_prs500_ls()
|
||||||
{
|
{
|
||||||
|
@ -32,6 +32,8 @@ What formats does |app| support conversion to/from?
|
|||||||
| | | | |
|
| | | | |
|
||||||
| | EPUB | ✔ | ✔ |
|
| | EPUB | ✔ | ✔ |
|
||||||
| | | | |
|
| | | | |
|
||||||
|
| | ODT | ✔ | ✔ |
|
||||||
|
| | | | |
|
||||||
| | HTML | ✔ | ✔ |
|
| | HTML | ✔ | ✔ |
|
||||||
| | | | |
|
| | | | |
|
||||||
| **Input formats** | CBR | ✔ | ✔ |
|
| **Input formats** | CBR | ✔ | ✔ |
|
||||||
|
0
src/odf/__init__.py
Normal file
0
src/odf/__init__.py
Normal file
61
src/odf/anim.py
Normal file
61
src/odf/anim.py
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
|
||||||
|
#
|
||||||
|
# This library is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU Lesser General Public
|
||||||
|
# License as published by the Free Software Foundation; either
|
||||||
|
# version 2.1 of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This library is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
# Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public
|
||||||
|
# License along with this library; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
#
|
||||||
|
# Contributor(s):
|
||||||
|
#
|
||||||
|
|
||||||
|
from namespaces import ANIMNS
|
||||||
|
from element import Element
|
||||||
|
|
||||||
|
|
||||||
|
# Autogenerated
|
||||||
|
def Animate(**args):
|
||||||
|
return Element(qname = (ANIMNS,'animate'), **args)
|
||||||
|
|
||||||
|
def Animatecolor(**args):
|
||||||
|
return Element(qname = (ANIMNS,'animateColor'), **args)
|
||||||
|
|
||||||
|
def Animatemotion(**args):
|
||||||
|
return Element(qname = (ANIMNS,'animateMotion'), **args)
|
||||||
|
|
||||||
|
def Animatetransform(**args):
|
||||||
|
return Element(qname = (ANIMNS,'animateTransform'), **args)
|
||||||
|
|
||||||
|
def Audio(**args):
|
||||||
|
return Element(qname = (ANIMNS,'audio'), **args)
|
||||||
|
|
||||||
|
def Command(**args):
|
||||||
|
return Element(qname = (ANIMNS,'command'), **args)
|
||||||
|
|
||||||
|
def Iterate(**args):
|
||||||
|
return Element(qname = (ANIMNS,'iterate'), **args)
|
||||||
|
|
||||||
|
def Par(**args):
|
||||||
|
return Element(qname = (ANIMNS,'par'), **args)
|
||||||
|
|
||||||
|
def Param(**args):
|
||||||
|
return Element(qname = (ANIMNS,'param'), **args)
|
||||||
|
|
||||||
|
def Seq(**args):
|
||||||
|
return Element(qname = (ANIMNS,'seq'), **args)
|
||||||
|
|
||||||
|
def Set(**args):
|
||||||
|
return Element(qname = (ANIMNS,'set'), **args)
|
||||||
|
|
||||||
|
def Transitionfilter(**args):
|
||||||
|
return Element(qname = (ANIMNS,'transitionFilter'), **args)
|
||||||
|
|
1444
src/odf/attrconverters.py
Normal file
1444
src/odf/attrconverters.py
Normal file
File diff suppressed because it is too large
Load Diff
87
src/odf/chart.py
Normal file
87
src/odf/chart.py
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
|
||||||
|
#
|
||||||
|
# This library is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU Lesser General Public
|
||||||
|
# License as published by the Free Software Foundation; either
|
||||||
|
# version 2.1 of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This library is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
# Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public
|
||||||
|
# License along with this library; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
#
|
||||||
|
# Contributor(s):
|
||||||
|
#
|
||||||
|
|
||||||
|
from namespaces import CHARTNS
|
||||||
|
from element import Element
|
||||||
|
|
||||||
|
# Autogenerated
|
||||||
|
def Axis(**args):
|
||||||
|
return Element(qname = (CHARTNS,'axis'), **args)
|
||||||
|
|
||||||
|
def Categories(**args):
|
||||||
|
return Element(qname = (CHARTNS,'categories'), **args)
|
||||||
|
|
||||||
|
def Chart(**args):
|
||||||
|
return Element(qname = (CHARTNS,'chart'), **args)
|
||||||
|
|
||||||
|
def DataPoint(**args):
|
||||||
|
return Element(qname = (CHARTNS,'data-point'), **args)
|
||||||
|
|
||||||
|
def Domain(**args):
|
||||||
|
return Element(qname = (CHARTNS,'domain'), **args)
|
||||||
|
|
||||||
|
def ErrorIndicator(**args):
|
||||||
|
return Element(qname = (CHARTNS,'error-indicator'), **args)
|
||||||
|
|
||||||
|
def Floor(**args):
|
||||||
|
return Element(qname = (CHARTNS,'floor'), **args)
|
||||||
|
|
||||||
|
def Footer(**args):
|
||||||
|
return Element(qname = (CHARTNS,'footer'), **args)
|
||||||
|
|
||||||
|
def Grid(**args):
|
||||||
|
return Element(qname = (CHARTNS,'grid'), **args)
|
||||||
|
|
||||||
|
def Legend(**args):
|
||||||
|
return Element(qname = (CHARTNS,'legend'), **args)
|
||||||
|
|
||||||
|
def MeanValue(**args):
|
||||||
|
return Element(qname = (CHARTNS,'mean-value'), **args)
|
||||||
|
|
||||||
|
def PlotArea(**args):
|
||||||
|
return Element(qname = (CHARTNS,'plot-area'), **args)
|
||||||
|
|
||||||
|
def RegressionCurve(**args):
|
||||||
|
return Element(qname = (CHARTNS,'regression-curve'), **args)
|
||||||
|
|
||||||
|
def Series(**args):
|
||||||
|
return Element(qname = (CHARTNS,'series'), **args)
|
||||||
|
|
||||||
|
def StockGainMarker(**args):
|
||||||
|
return Element(qname = (CHARTNS,'stock-gain-marker'), **args)
|
||||||
|
|
||||||
|
def StockLossMarker(**args):
|
||||||
|
return Element(qname = (CHARTNS,'stock-loss-marker'), **args)
|
||||||
|
|
||||||
|
def StockRangeLine(**args):
|
||||||
|
return Element(qname = (CHARTNS,'stock-range-line'), **args)
|
||||||
|
|
||||||
|
def Subtitle(**args):
|
||||||
|
return Element(qname = (CHARTNS,'subtitle'), **args)
|
||||||
|
|
||||||
|
def SymbolImage(**args):
|
||||||
|
return Element(qname = (CHARTNS,'symbol-image'), **args)
|
||||||
|
|
||||||
|
def Title(**args):
|
||||||
|
return Element(qname = (CHARTNS,'title'), **args)
|
||||||
|
|
||||||
|
def Wall(**args):
|
||||||
|
return Element(qname = (CHARTNS,'wall'), **args)
|
||||||
|
|
39
src/odf/config.py
Normal file
39
src/odf/config.py
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
|
||||||
|
#
|
||||||
|
# This library is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU Lesser General Public
|
||||||
|
# License as published by the Free Software Foundation; either
|
||||||
|
# version 2.1 of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This library is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
# Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public
|
||||||
|
# License along with this library; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
#
|
||||||
|
# Contributor(s):
|
||||||
|
#
|
||||||
|
|
||||||
|
from namespaces import CONFIGNS
|
||||||
|
from element import Element
|
||||||
|
|
||||||
|
# Autogenerated
|
||||||
|
def ConfigItem(**args):
|
||||||
|
return Element(qname = (CONFIGNS, 'config-item'), **args)
|
||||||
|
|
||||||
|
def ConfigItemMapEntry(**args):
|
||||||
|
return Element(qname = (CONFIGNS,'config-item-map-entry'), **args)
|
||||||
|
|
||||||
|
def ConfigItemMapIndexed(**args):
|
||||||
|
return Element(qname = (CONFIGNS,'config-item-map-indexed'), **args)
|
||||||
|
|
||||||
|
def ConfigItemMapNamed(**args):
|
||||||
|
return Element(qname = (CONFIGNS,'config-item-map-named'), **args)
|
||||||
|
|
||||||
|
def ConfigItemSet(**args):
|
||||||
|
return Element(qname = (CONFIGNS, 'config-item-set'), **args)
|
||||||
|
|
72
src/odf/dc.py
Normal file
72
src/odf/dc.py
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
|
||||||
|
#
|
||||||
|
# This library is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU Lesser General Public
|
||||||
|
# License as published by the Free Software Foundation; either
|
||||||
|
# version 2.1 of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This library is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
# Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public
|
||||||
|
# License along with this library; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
#
|
||||||
|
# Contributor(s):
|
||||||
|
#
|
||||||
|
|
||||||
|
from namespaces import DCNS
|
||||||
|
from element import Element
|
||||||
|
|
||||||
|
# Autogenerated
|
||||||
|
def Creator(**args):
|
||||||
|
return Element(qname = (DCNS,'creator'), **args)
|
||||||
|
|
||||||
|
def Date(**args):
|
||||||
|
return Element(qname = (DCNS,'date'), **args)
|
||||||
|
|
||||||
|
def Description(**args):
|
||||||
|
return Element(qname = (DCNS,'description'), **args)
|
||||||
|
|
||||||
|
def Language(**args):
|
||||||
|
return Element(qname = (DCNS,'language'), **args)
|
||||||
|
|
||||||
|
def Subject(**args):
|
||||||
|
return Element(qname = (DCNS,'subject'), **args)
|
||||||
|
|
||||||
|
def Title(**args):
|
||||||
|
return Element(qname = (DCNS,'title'), **args)
|
||||||
|
|
||||||
|
# The following complete the Dublin Core elements, but there is no
|
||||||
|
# guarantee a compliant implementation of OpenDocument will preserve
|
||||||
|
# these elements
|
||||||
|
|
||||||
|
#def Contributor(**args):
|
||||||
|
# return Element(qname = (DCNS,'contributor'), **args)
|
||||||
|
|
||||||
|
#def Coverage(**args):
|
||||||
|
# return Element(qname = (DCNS,'coverage'), **args)
|
||||||
|
|
||||||
|
#def Format(**args):
|
||||||
|
# return Element(qname = (DCNS,'format'), **args)
|
||||||
|
|
||||||
|
#def Identifier(**args):
|
||||||
|
# return Element(qname = (DCNS,'identifier'), **args)
|
||||||
|
|
||||||
|
#def Publisher(**args):
|
||||||
|
# return Element(qname = (DCNS,'publisher'), **args)
|
||||||
|
|
||||||
|
#def Relation(**args):
|
||||||
|
# return Element(qname = (DCNS,'relation'), **args)
|
||||||
|
|
||||||
|
#def Rights(**args):
|
||||||
|
# return Element(qname = (DCNS,'rights'), **args)
|
||||||
|
|
||||||
|
#def Source(**args):
|
||||||
|
# return Element(qname = (DCNS,'source'), **args)
|
||||||
|
|
||||||
|
#def Type(**args):
|
||||||
|
# return Element(qname = (DCNS,'type'), **args)
|
43
src/odf/dr3d.py
Normal file
43
src/odf/dr3d.py
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
|
||||||
|
#
|
||||||
|
# This library is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU Lesser General Public
|
||||||
|
# License as published by the Free Software Foundation; either
|
||||||
|
# version 2.1 of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This library is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
# Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public
|
||||||
|
# License along with this library; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
#
|
||||||
|
# Contributor(s):
|
||||||
|
#
|
||||||
|
|
||||||
|
from namespaces import DR3DNS
|
||||||
|
from element import Element
|
||||||
|
from draw import StyleRefElement
|
||||||
|
|
||||||
|
# Autogenerated
|
||||||
|
def Cube(**args):
|
||||||
|
return StyleRefElement(qname = (DR3DNS,'cube'), **args)
|
||||||
|
|
||||||
|
def Extrude(**args):
|
||||||
|
return StyleRefElement(qname = (DR3DNS,'extrude'), **args)
|
||||||
|
|
||||||
|
def Light(Element):
|
||||||
|
return StyleRefElement(qname = (DR3DNS,'light'), **args)
|
||||||
|
|
||||||
|
def Rotate(**args):
|
||||||
|
return StyleRefElement(qname = (DR3DNS,'rotate'), **args)
|
||||||
|
|
||||||
|
def Scene(**args):
|
||||||
|
return StyleRefElement(qname = (DR3DNS,'scene'), **args)
|
||||||
|
|
||||||
|
def Sphere(**args):
|
||||||
|
return StyleRefElement(qname = (DR3DNS,'sphere'), **args)
|
||||||
|
|
182
src/odf/draw.py
Normal file
182
src/odf/draw.py
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
|
||||||
|
#
|
||||||
|
# This library is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU Lesser General Public
|
||||||
|
# License as published by the Free Software Foundation; either
|
||||||
|
# version 2.1 of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This library is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
# Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public
|
||||||
|
# License along with this library; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
#
|
||||||
|
# Contributor(s):
|
||||||
|
#
|
||||||
|
|
||||||
|
from namespaces import DRAWNS, STYLENS, PRESENTATIONNS
|
||||||
|
from element import Element
|
||||||
|
|
||||||
|
def StyleRefElement(stylename=None, classnames=None, **args):
|
||||||
|
qattrs = {}
|
||||||
|
if stylename is not None:
|
||||||
|
f = stylename.getAttrNS(STYLENS, 'family')
|
||||||
|
if f == 'graphic':
|
||||||
|
qattrs[(DRAWNS,u'style-name')]= stylename
|
||||||
|
elif f == 'presentation':
|
||||||
|
qattrs[(PRESENTATIONNS,u'style-name')]= stylename
|
||||||
|
else:
|
||||||
|
raise ValueError, "Style's family must be either 'graphic' or 'presentation'"
|
||||||
|
if classnames is not None:
|
||||||
|
f = classnames[0].getAttrNS(STYLENS, 'family')
|
||||||
|
if f == 'graphic':
|
||||||
|
qattrs[(DRAWNS,u'class-names')]= classnames
|
||||||
|
elif f == 'presentation':
|
||||||
|
qattrs[(PRESENTATIONNS,u'class-names')]= classnames
|
||||||
|
else:
|
||||||
|
raise ValueError, "Style's family must be either 'graphic' or 'presentation'"
|
||||||
|
return Element(qattributes=qattrs, **args)
|
||||||
|
|
||||||
|
def DrawElement(name=None, **args):
|
||||||
|
e = Element(name=name, **args)
|
||||||
|
if not args.has_key('displayname'):
|
||||||
|
e.setAttrNS(DRAWNS,'display-name', name)
|
||||||
|
return e
|
||||||
|
|
||||||
|
# Autogenerated
|
||||||
|
def A(**args):
|
||||||
|
return Element(qname = (DRAWNS,'a'), **args)
|
||||||
|
|
||||||
|
def Applet(**args):
|
||||||
|
return Element(qname = (DRAWNS,'applet'), **args)
|
||||||
|
|
||||||
|
def AreaCircle(**args):
|
||||||
|
return Element(qname = (DRAWNS,'area-circle'), **args)
|
||||||
|
|
||||||
|
def AreaPolygon(**args):
|
||||||
|
return Element(qname = (DRAWNS,'area-polygon'), **args)
|
||||||
|
|
||||||
|
def AreaRectangle(**args):
|
||||||
|
return Element(qname = (DRAWNS,'area-rectangle'), **args)
|
||||||
|
|
||||||
|
def Caption(**args):
|
||||||
|
return StyleRefElement(qname = (DRAWNS,'caption'), **args)
|
||||||
|
|
||||||
|
def Circle(**args):
|
||||||
|
return StyleRefElement(qname = (DRAWNS,'circle'), **args)
|
||||||
|
|
||||||
|
def Connector(**args):
|
||||||
|
return StyleRefElement(qname = (DRAWNS,'connector'), **args)
|
||||||
|
|
||||||
|
def ContourPath(**args):
|
||||||
|
return Element(qname = (DRAWNS,'contour-path'), **args)
|
||||||
|
|
||||||
|
def ContourPolygon(**args):
|
||||||
|
return Element(qname = (DRAWNS,'contour-polygon'), **args)
|
||||||
|
|
||||||
|
def Control(**args):
|
||||||
|
return StyleRefElement(qname = (DRAWNS,'control'), **args)
|
||||||
|
|
||||||
|
def CustomShape(**args):
|
||||||
|
return StyleRefElement(qname = (DRAWNS,'custom-shape'), **args)
|
||||||
|
|
||||||
|
def Ellipse(**args):
|
||||||
|
return StyleRefElement(qname = (DRAWNS,'ellipse'), **args)
|
||||||
|
|
||||||
|
def EnhancedGeometry(**args):
|
||||||
|
return Element(qname = (DRAWNS,'enhanced-geometry'), **args)
|
||||||
|
|
||||||
|
def Equation(**args):
|
||||||
|
return Element(qname = (DRAWNS,'equation'), **args)
|
||||||
|
|
||||||
|
def FillImage(**args):
|
||||||
|
return DrawElement(qname = (DRAWNS,'fill-image'), **args)
|
||||||
|
|
||||||
|
def FloatingFrame(**args):
|
||||||
|
return Element(qname = (DRAWNS,'floating-frame'), **args)
|
||||||
|
|
||||||
|
def Frame(**args):
|
||||||
|
return StyleRefElement(qname = (DRAWNS,'frame'), **args)
|
||||||
|
|
||||||
|
def G(**args):
|
||||||
|
return StyleRefElement(qname = (DRAWNS,'g'), **args)
|
||||||
|
|
||||||
|
def GluePoint(**args):
|
||||||
|
return Element(qname = (DRAWNS,'glue-point'), **args)
|
||||||
|
|
||||||
|
def Gradient(**args):
|
||||||
|
return DrawElement(qname = (DRAWNS,'gradient'), **args)
|
||||||
|
|
||||||
|
def Handle(**args):
|
||||||
|
return Element(qname = (DRAWNS,'handle'), **args)
|
||||||
|
|
||||||
|
def Hatch(**args):
|
||||||
|
return DrawElement(qname = (DRAWNS,'hatch'), **args)
|
||||||
|
|
||||||
|
def Image(**args):
|
||||||
|
return Element(qname = (DRAWNS,'image'), **args)
|
||||||
|
|
||||||
|
def ImageMap(**args):
|
||||||
|
return Element(qname = (DRAWNS,'image-map'), **args)
|
||||||
|
|
||||||
|
def Layer(**args):
|
||||||
|
return Element(qname = (DRAWNS,'layer'), **args)
|
||||||
|
|
||||||
|
def LayerSet(**args):
|
||||||
|
return Element(qname = (DRAWNS,'layer-set'), **args)
|
||||||
|
|
||||||
|
def Line(**args):
|
||||||
|
return StyleRefElement(qname = (DRAWNS,'line'), **args)
|
||||||
|
|
||||||
|
def Marker(**args):
|
||||||
|
return DrawElement(qname = (DRAWNS,'marker'), **args)
|
||||||
|
|
||||||
|
def Measure(**args):
|
||||||
|
return StyleRefElement(qname = (DRAWNS,'measure'), **args)
|
||||||
|
|
||||||
|
def Object(**args):
|
||||||
|
return Element(qname = (DRAWNS,'object'), **args)
|
||||||
|
|
||||||
|
def ObjectOle(**args):
|
||||||
|
return Element(qname = (DRAWNS,'object-ole'), **args)
|
||||||
|
|
||||||
|
def Opacity(**args):
|
||||||
|
return DrawElement(qname = (DRAWNS,'opacity'), **args)
|
||||||
|
|
||||||
|
def Page(**args):
|
||||||
|
return Element(qname = (DRAWNS,'page'), **args)
|
||||||
|
|
||||||
|
def PageThumbnail(**args):
|
||||||
|
return StyleRefElement(qname = (DRAWNS,'page-thumbnail'), **args)
|
||||||
|
|
||||||
|
def Param(**args):
|
||||||
|
return Element(qname = (DRAWNS,'param'), **args)
|
||||||
|
|
||||||
|
def Path(**args):
|
||||||
|
return StyleRefElement(qname = (DRAWNS,'path'), **args)
|
||||||
|
|
||||||
|
def Plugin(**args):
|
||||||
|
return Element(qname = (DRAWNS,'plugin'), **args)
|
||||||
|
|
||||||
|
def Polygon(**args):
|
||||||
|
return StyleRefElement(qname = (DRAWNS,'polygon'), **args)
|
||||||
|
|
||||||
|
def Polyline(**args):
|
||||||
|
return StyleRefElement(qname = (DRAWNS,'polyline'), **args)
|
||||||
|
|
||||||
|
def Rect(**args):
|
||||||
|
return StyleRefElement(qname = (DRAWNS,'rect'), **args)
|
||||||
|
|
||||||
|
def RegularPolygon(**args):
|
||||||
|
return StyleRefElement(qname = (DRAWNS,'regular-polygon'), **args)
|
||||||
|
|
||||||
|
def StrokeDash(**args):
|
||||||
|
return DrawElement(qname = (DRAWNS,'stroke-dash'), **args)
|
||||||
|
|
||||||
|
def TextBox(**args):
|
||||||
|
return Element(qname = (DRAWNS,'text-box'), **args)
|
||||||
|
|
103
src/odf/easyliststyle.py
Normal file
103
src/odf/easyliststyle.py
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Create a <text:list-style> element from a text string.
|
||||||
|
# Copyright (C) 2008 J. David Eisenberg
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License along
|
||||||
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
#
|
||||||
|
# Contributor(s):
|
||||||
|
#
|
||||||
|
|
||||||
|
import re
|
||||||
|
from style import Style, TextProperties, ListLevelProperties
|
||||||
|
from text import ListStyle,ListLevelStyleNumber,ListLevelStyleBullet
|
||||||
|
|
||||||
|
"""
|
||||||
|
Create a <text:list-style> element from a string or array.
|
||||||
|
|
||||||
|
List styles require a lot of code to create one level at a time.
|
||||||
|
These routines take a string and delimiter, or a list of
|
||||||
|
strings, and creates a <text:list-style> element for you.
|
||||||
|
Each item in the string (or array) represents a list level
|
||||||
|
* style for levels 1-10.</p>
|
||||||
|
*
|
||||||
|
* <p>If an item contains <code>1</code>, <code>I</code>,
|
||||||
|
* <code>i</code>, <code>A</code>, or <code>a</code>, then it is presumed
|
||||||
|
* to be a numbering style; otherwise it is a bulleted style.</p>
|
||||||
|
"""
|
||||||
|
|
||||||
|
_MAX_LIST_LEVEL = 10
|
||||||
|
SHOW_ALL_LEVELS = True
|
||||||
|
SHOW_ONE_LEVEL = False
|
||||||
|
|
||||||
|
def styleFromString(name, specifiers, delim, spacing, showAllLevels):
|
||||||
|
specArray = specifiers.split(delim)
|
||||||
|
return styleFromList( name, specArray, spacing, showAllLevels )
|
||||||
|
|
||||||
|
def styleFromList( styleName, specArray, spacing, showAllLevels):
|
||||||
|
bullet = ""
|
||||||
|
numPrefix = ""
|
||||||
|
numSuffix = ""
|
||||||
|
numberFormat = ""
|
||||||
|
cssLengthNum = 0
|
||||||
|
cssLengthUnits = ""
|
||||||
|
numbered = False
|
||||||
|
displayLevels = 0
|
||||||
|
listStyle = ListStyle(name=styleName)
|
||||||
|
numFormatPattern = re.compile("([1IiAa])")
|
||||||
|
cssLengthPattern = re.compile("([^a-z]+)\\s*([a-z]+)?")
|
||||||
|
m = cssLengthPattern.search( spacing )
|
||||||
|
if (m != None):
|
||||||
|
cssLengthNum = float(m.group(1))
|
||||||
|
if (m.lastindex == 2):
|
||||||
|
cssLengthUnits = m.group(2)
|
||||||
|
i = 0
|
||||||
|
while i < len(specArray):
|
||||||
|
specification = specArray[i]
|
||||||
|
m = numFormatPattern.search(specification)
|
||||||
|
if (m != None):
|
||||||
|
numberFormat = m.group(1)
|
||||||
|
numPrefix = specification[0:m.start(1)]
|
||||||
|
numSuffix = specification[m.end(1):]
|
||||||
|
bullet = ""
|
||||||
|
numbered = True
|
||||||
|
if (showAllLevels):
|
||||||
|
displayLevels = i + 1
|
||||||
|
else:
|
||||||
|
displayLevels = 1
|
||||||
|
else: # it's a bullet style
|
||||||
|
bullet = specification
|
||||||
|
numPrefix = ""
|
||||||
|
numSuffix = ""
|
||||||
|
numberFormat = ""
|
||||||
|
displayLevels = 1
|
||||||
|
numbered = False
|
||||||
|
if (numbered):
|
||||||
|
lls = ListLevelStyleNumber(level=(i+1))
|
||||||
|
if (numPrefix != ''):
|
||||||
|
lls.setAttribute('numprefix', numPrefix)
|
||||||
|
if (numSuffix != ''):
|
||||||
|
lls.setAttribute('numsuffix', numSuffix)
|
||||||
|
lls.setAttribute('displaylevels', displayLevels)
|
||||||
|
else:
|
||||||
|
lls = ListLevelStyleBullet(level=(i+1),bulletchar=bullet[0])
|
||||||
|
llp = ListLevelProperties()
|
||||||
|
llp.setAttribute('spacebefore', str(cssLengthNum * (i+1)) + cssLengthUnits)
|
||||||
|
llp.setAttribute('minlabelwidth', str(cssLengthNum) + cssLengthUnits)
|
||||||
|
lls.addElement( llp )
|
||||||
|
listStyle.addElement(lls)
|
||||||
|
i += 1
|
||||||
|
return listStyle
|
||||||
|
|
||||||
|
# vim: set expandtab sw=4 :
|
454
src/odf/element.py
Normal file
454
src/odf/element.py
Normal file
@ -0,0 +1,454 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2007-2008 Søren Roug, European Environment Agency
|
||||||
|
#
|
||||||
|
# This library is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU Lesser General Public
|
||||||
|
# License as published by the Free Software Foundation; either
|
||||||
|
# version 2.1 of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This library is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
# Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public
|
||||||
|
# License along with this library; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
#
|
||||||
|
# Contributor(s):
|
||||||
|
#
|
||||||
|
|
||||||
|
# Note: This script has copied a lot of text from xml.dom.minidom.
|
||||||
|
# Whatever license applies to that file also applies to this file.
|
||||||
|
#
|
||||||
|
import xml.dom
|
||||||
|
from xml.dom.minicompat import *
|
||||||
|
from namespaces import nsdict
|
||||||
|
import grammar
|
||||||
|
from attrconverters import AttrConverters
|
||||||
|
|
||||||
|
# The following code is pasted form xml.sax.saxutils
|
||||||
|
# Tt makes it possible to run the code without the xml sax package installed
|
||||||
|
# To make it possible to have <rubbish> in your text elements, it is necessary to escape the texts
|
||||||
|
def _escape(data, entities={}):
|
||||||
|
""" Escape &, <, and > in a string of data.
|
||||||
|
|
||||||
|
You can escape other strings of data by passing a dictionary as
|
||||||
|
the optional entities parameter. The keys and values must all be
|
||||||
|
strings; each key will be replaced with its corresponding value.
|
||||||
|
"""
|
||||||
|
data = data.replace("&", "&")
|
||||||
|
data = data.replace("<", "<")
|
||||||
|
data = data.replace(">", ">")
|
||||||
|
for chars, entity in entities.items():
|
||||||
|
data = data.replace(chars, entity)
|
||||||
|
return data
|
||||||
|
|
||||||
|
def _quoteattr(data, entities={}):
|
||||||
|
""" Escape and quote an attribute value.
|
||||||
|
|
||||||
|
Escape &, <, and > in a string of data, then quote it for use as
|
||||||
|
an attribute value. The \" character will be escaped as well, if
|
||||||
|
necessary.
|
||||||
|
|
||||||
|
You can escape other strings of data by passing a dictionary as
|
||||||
|
the optional entities parameter. The keys and values must all be
|
||||||
|
strings; each key will be replaced with its corresponding value.
|
||||||
|
"""
|
||||||
|
data = _escape(data, entities)
|
||||||
|
if '"' in data:
|
||||||
|
if "'" in data:
|
||||||
|
data = '"%s"' % data.replace('"', """)
|
||||||
|
else:
|
||||||
|
data = "'%s'" % data
|
||||||
|
else:
|
||||||
|
data = '"%s"' % data
|
||||||
|
return data
|
||||||
|
|
||||||
|
def _nssplit(qualifiedName):
|
||||||
|
""" Split a qualified name into namespace part and local part. """
|
||||||
|
fields = qualifiedName.split(':', 1)
|
||||||
|
if len(fields) == 2:
|
||||||
|
return fields
|
||||||
|
else:
|
||||||
|
return (None, fields[0])
|
||||||
|
|
||||||
|
def _nsassign(namespace):
|
||||||
|
return nsdict.setdefault(namespace,"ns" + str(len(nsdict)))
|
||||||
|
|
||||||
|
# Exceptions
|
||||||
|
class IllegalChild(StandardError):
|
||||||
|
""" Complains if you add an element to a parent where it is not allowed """
|
||||||
|
class IllegalText(StandardError):
|
||||||
|
""" Complains if you add text or cdata to an element where it is not allowed """
|
||||||
|
|
||||||
|
class Node(xml.dom.Node):
|
||||||
|
""" super class for more specific nodes """
|
||||||
|
parentNode = None
|
||||||
|
nextSibling = None
|
||||||
|
previousSibling = None
|
||||||
|
|
||||||
|
def hasChildNodes(self):
|
||||||
|
""" Tells whether this element has any children; text nodes,
|
||||||
|
subelements, whatever.
|
||||||
|
"""
|
||||||
|
if self.childNodes:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _get_childNodes(self):
|
||||||
|
return self.childNodes
|
||||||
|
|
||||||
|
def _get_firstChild(self):
|
||||||
|
if self.childNodes:
|
||||||
|
return self.childNodes[0]
|
||||||
|
|
||||||
|
def _get_lastChild(self):
|
||||||
|
if self.childNodes:
|
||||||
|
return self.childNodes[-1]
|
||||||
|
|
||||||
|
def insertBefore(self, newChild, refChild):
|
||||||
|
if newChild.nodeType not in self._child_node_types:
|
||||||
|
raise IllegalChild, "%s cannot be child of %s" % (newChild.tagName, self.tagName)
|
||||||
|
if newChild.parentNode is not None:
|
||||||
|
newChild.parentNode.removeChild(newChild)
|
||||||
|
if refChild is None:
|
||||||
|
self.appendChild(newChild)
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
index = self.childNodes.index(refChild)
|
||||||
|
except ValueError:
|
||||||
|
raise xml.dom.NotFoundErr()
|
||||||
|
self.childNodes.insert(index, newChild)
|
||||||
|
newChild.nextSibling = refChild
|
||||||
|
refChild.previousSibling = newChild
|
||||||
|
if index:
|
||||||
|
node = self.childNodes[index-1]
|
||||||
|
node.nextSibling = newChild
|
||||||
|
newChild.previousSibling = node
|
||||||
|
else:
|
||||||
|
newChild.previousSibling = None
|
||||||
|
newChild.parentNode = self
|
||||||
|
return newChild
|
||||||
|
|
||||||
|
def appendChild(self, node):
|
||||||
|
if node.nodeType == self.DOCUMENT_FRAGMENT_NODE:
|
||||||
|
for c in tuple(node.childNodes):
|
||||||
|
self.appendChild(c)
|
||||||
|
### The DOM does not clearly specify what to return in this case
|
||||||
|
return node
|
||||||
|
if node.nodeType not in self._child_node_types:
|
||||||
|
raise IllegalChild, "<%s> is not allowed in %s" % ( node.tagName, self.tagName)
|
||||||
|
if node.parentNode is not None:
|
||||||
|
node.parentNode.removeChild(node)
|
||||||
|
_append_child(self, node)
|
||||||
|
node.nextSibling = None
|
||||||
|
return node
|
||||||
|
|
||||||
|
def removeChild(self, oldChild):
|
||||||
|
#FIXME: update ownerDocument.element_dict or find other solution
|
||||||
|
try:
|
||||||
|
self.childNodes.remove(oldChild)
|
||||||
|
except ValueError:
|
||||||
|
raise xml.dom.NotFoundErr()
|
||||||
|
if oldChild.nextSibling is not None:
|
||||||
|
oldChild.nextSibling.previousSibling = oldChild.previousSibling
|
||||||
|
if oldChild.previousSibling is not None:
|
||||||
|
oldChild.previousSibling.nextSibling = oldChild.nextSibling
|
||||||
|
oldChild.nextSibling = oldChild.previousSibling = None
|
||||||
|
if self.ownerDocument:
|
||||||
|
self.ownerDocument.clear_caches()
|
||||||
|
oldChild.parentNode = None
|
||||||
|
return oldChild
|
||||||
|
|
||||||
|
defproperty(Node, "firstChild", doc="First child node, or None.")
|
||||||
|
defproperty(Node, "lastChild", doc="Last child node, or None.")
|
||||||
|
|
||||||
|
def _append_child(self, node):
|
||||||
|
# fast path with less checks; usable by DOM builders if careful
|
||||||
|
childNodes = self.childNodes
|
||||||
|
if childNodes:
|
||||||
|
last = childNodes[-1]
|
||||||
|
node.__dict__["previousSibling"] = last
|
||||||
|
last.__dict__["nextSibling"] = node
|
||||||
|
childNodes.append(node)
|
||||||
|
node.__dict__["parentNode"] = self
|
||||||
|
|
||||||
|
class Childless:
|
||||||
|
"""Mixin that makes childless-ness easy to implement and avoids
|
||||||
|
the complexity of the Node methods that deal with children.
|
||||||
|
"""
|
||||||
|
|
||||||
|
attributes = None
|
||||||
|
childNodes = EmptyNodeList()
|
||||||
|
firstChild = None
|
||||||
|
lastChild = None
|
||||||
|
|
||||||
|
def _get_firstChild(self):
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _get_lastChild(self):
|
||||||
|
return None
|
||||||
|
|
||||||
|
def appendChild(self, node):
|
||||||
|
raise xml.dom.HierarchyRequestErr(
|
||||||
|
self.tagName + " nodes cannot have children")
|
||||||
|
|
||||||
|
def hasChildNodes(self):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def insertBefore(self, newChild, refChild):
|
||||||
|
raise xml.dom.HierarchyRequestErr(
|
||||||
|
self.tagName + " nodes do not have children")
|
||||||
|
|
||||||
|
def removeChild(self, oldChild):
|
||||||
|
raise xml.dom.NotFoundErr(
|
||||||
|
self.tagName + " nodes do not have children")
|
||||||
|
|
||||||
|
def replaceChild(self, newChild, oldChild):
|
||||||
|
raise xml.dom.HierarchyRequestErr(
|
||||||
|
self.tagName + " nodes do not have children")
|
||||||
|
|
||||||
|
class Text(Childless, Node):
|
||||||
|
nodeType = Node.TEXT_NODE
|
||||||
|
tagName = "Text"
|
||||||
|
|
||||||
|
def __init__(self, data):
|
||||||
|
self.data = data
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.data
|
||||||
|
|
||||||
|
def toXml(self,level,f):
|
||||||
|
""" Write XML in UTF-8 """
|
||||||
|
if self.data:
|
||||||
|
f.write(_escape(unicode(self.data).encode('utf-8')))
|
||||||
|
|
||||||
|
class CDATASection(Childless, Text):
|
||||||
|
nodeType = Node.CDATA_SECTION_NODE
|
||||||
|
|
||||||
|
def toXml(self,level,f):
|
||||||
|
if self.data:
|
||||||
|
f.write('<![CDATA[%s]]>' % self.data)
|
||||||
|
|
||||||
|
class Element(Node):
|
||||||
|
""" Creates a arbitrary element and is intended to be subclassed not used on its own.
|
||||||
|
This element is the base of every element it defines a class which resembles
|
||||||
|
a xml-element. The main advantage of this kind of implementation is that you don't
|
||||||
|
have to create a toXML method for every different object. Every element
|
||||||
|
consists of an attribute, optional subelements, optional text and optional cdata.
|
||||||
|
"""
|
||||||
|
|
||||||
|
nodeType = Node.ELEMENT_NODE
|
||||||
|
namespaces = {} # Due to shallow copy this is a static variable
|
||||||
|
|
||||||
|
_child_node_types = (Node.ELEMENT_NODE,
|
||||||
|
Node.PROCESSING_INSTRUCTION_NODE,
|
||||||
|
Node.COMMENT_NODE,
|
||||||
|
Node.TEXT_NODE,
|
||||||
|
Node.CDATA_SECTION_NODE,
|
||||||
|
Node.ENTITY_REFERENCE_NODE)
|
||||||
|
|
||||||
|
def __init__(self, attributes=None, text=None, cdata=None, qname=None, qattributes=None, check_grammar=True, **args):
|
||||||
|
if qname is not None:
|
||||||
|
self.qname = qname
|
||||||
|
assert(hasattr(self, 'qname'))
|
||||||
|
self.ownerDocument = None
|
||||||
|
self.childNodes=[]
|
||||||
|
self.allowed_children = grammar.allowed_children.get(self.qname)
|
||||||
|
namespace = self.qname[0]
|
||||||
|
prefix = _nsassign(namespace)
|
||||||
|
if not self.namespaces.has_key(namespace):
|
||||||
|
self.namespaces[namespace] = prefix
|
||||||
|
self.tagName = prefix + ":" + self.qname[1]
|
||||||
|
if text is not None:
|
||||||
|
self.addText(text)
|
||||||
|
if cdata is not None:
|
||||||
|
self.addCDATA(cdata)
|
||||||
|
|
||||||
|
allowed_attrs = self.allowed_attributes()
|
||||||
|
if allowed_attrs is not None:
|
||||||
|
allowed_args = [ a[1].lower().replace('-','') for a in allowed_attrs]
|
||||||
|
self.attributes={}
|
||||||
|
# Load the attributes from the 'attributes' argument
|
||||||
|
if attributes:
|
||||||
|
for attr, value in attributes.items():
|
||||||
|
self.setAttribute(attr, value)
|
||||||
|
# Load the qualified attributes
|
||||||
|
if qattributes:
|
||||||
|
for attr, value in qattributes.items():
|
||||||
|
self.setAttrNS(attr[0], attr[1], value)
|
||||||
|
if allowed_attrs is not None:
|
||||||
|
# Load the attributes from the 'args' argument
|
||||||
|
for arg in args.keys():
|
||||||
|
self.setAttribute(arg, args[arg])
|
||||||
|
else:
|
||||||
|
for arg in args.keys(): # If any attribute is allowed
|
||||||
|
self.attributes[arg]=args[arg]
|
||||||
|
if not check_grammar:
|
||||||
|
return
|
||||||
|
# Test that all mandatory attributes have been added.
|
||||||
|
required = grammar.required_attributes.get(self.qname)
|
||||||
|
if required:
|
||||||
|
for r in required:
|
||||||
|
if self.getAttrNS(r[0],r[1]) is None:
|
||||||
|
raise AttributeError, "Required attribute missing: %s in <%s>" % (r[1].lower().replace('-',''), self.tagName)
|
||||||
|
|
||||||
|
def allowed_attributes(self):
|
||||||
|
return grammar.allowed_attributes.get(self.qname)
|
||||||
|
|
||||||
|
def _setOwnerDoc(self, element):
|
||||||
|
element.ownerDocument = self.ownerDocument
|
||||||
|
for child in element.childNodes:
|
||||||
|
self._setOwnerDoc(child)
|
||||||
|
|
||||||
|
def addElement(self, element, check_grammar=True):
|
||||||
|
""" adds an element to an Element
|
||||||
|
|
||||||
|
Element.addElement(Element)
|
||||||
|
"""
|
||||||
|
if check_grammar and self.allowed_children is not None:
|
||||||
|
if element.qname not in self.allowed_children:
|
||||||
|
raise IllegalChild, "<%s> is not allowed in <%s>" % ( element.tagName, self.tagName)
|
||||||
|
self.appendChild(element)
|
||||||
|
self._setOwnerDoc(element)
|
||||||
|
if self.ownerDocument:
|
||||||
|
self.ownerDocument.rebuild_caches(element)
|
||||||
|
|
||||||
|
def addText(self, text, check_grammar=True):
|
||||||
|
if check_grammar and self.qname not in grammar.allows_text:
|
||||||
|
raise IllegalText, "The <%s> element does not allow text" % self.tagName
|
||||||
|
else:
|
||||||
|
if text != '':
|
||||||
|
self.appendChild(Text(text))
|
||||||
|
|
||||||
|
def addCDATA(self, cdata, check_grammar=True):
|
||||||
|
if check_grammar and self.qname not in grammar.allows_text:
|
||||||
|
raise IllegalText, "The <%s> element does not allow text" % self.tagName
|
||||||
|
else:
|
||||||
|
self.appendChild(CDATASection(cdata))
|
||||||
|
|
||||||
|
def removeAttribute(self, attr, check_grammar=True):
|
||||||
|
""" Removes an attribute by name. """
|
||||||
|
allowed_attrs = self.allowed_attributes()
|
||||||
|
if allowed_attrs is None:
|
||||||
|
if type(attr) == type(()):
|
||||||
|
prefix, localname = attr
|
||||||
|
self.removeAttrNS(prefix, localname)
|
||||||
|
else:
|
||||||
|
raise AttributeError, "Unable to add simple attribute - use (namespace, localpart)"
|
||||||
|
else:
|
||||||
|
# Construct a list of allowed arguments
|
||||||
|
allowed_args = [ a[1].lower().replace('-','') for a in allowed_attrs]
|
||||||
|
if check_grammar and attr not in allowed_args:
|
||||||
|
raise AttributeError, "Attribute %s is not allowed in <%s>" % ( attr, self.tagName)
|
||||||
|
i = allowed_args.index(attr)
|
||||||
|
self.removeAttrNS(allowed_attrs[i][0], allowed_attrs[i][1])
|
||||||
|
|
||||||
|
def setAttribute(self, attr, value, check_grammar=True):
|
||||||
|
""" Add an attribute to the element
|
||||||
|
This is sort of a convenience method. All attributes in ODF have
|
||||||
|
namespaces. The library knows what attributes are legal and then allows
|
||||||
|
the user to provide the attribute as a keyword argument and the
|
||||||
|
library will add the correct namespace.
|
||||||
|
Must overwrite, If attribute already exists.
|
||||||
|
"""
|
||||||
|
allowed_attrs = self.allowed_attributes()
|
||||||
|
if allowed_attrs is None:
|
||||||
|
if type(attr) == type(()):
|
||||||
|
prefix, localname = attr
|
||||||
|
self.setAttrNS(prefix, localname, value)
|
||||||
|
else:
|
||||||
|
raise AttributeError, "Unable to add simple attribute - use (namespace, localpart)"
|
||||||
|
else:
|
||||||
|
# Construct a list of allowed arguments
|
||||||
|
allowed_args = [ a[1].lower().replace('-','') for a in allowed_attrs]
|
||||||
|
if check_grammar and attr not in allowed_args:
|
||||||
|
raise AttributeError, "Attribute %s is not allowed in <%s>" % ( attr, self.tagName)
|
||||||
|
i = allowed_args.index(attr)
|
||||||
|
self.setAttrNS(allowed_attrs[i][0], allowed_attrs[i][1], value)
|
||||||
|
|
||||||
|
def setAttrNS(self, namespace, localpart, value):
|
||||||
|
""" Add an attribute to the element
|
||||||
|
In case you need to add an attribute the library doesn't know about
|
||||||
|
then you must provide the full qualified name
|
||||||
|
It will not check that the attribute is legal according to the schema.
|
||||||
|
Must overwrite, If attribute already exists.
|
||||||
|
"""
|
||||||
|
allowed_attrs = self.allowed_attributes()
|
||||||
|
prefix = _nsassign(namespace)
|
||||||
|
if not self.namespaces.has_key(namespace):
|
||||||
|
self.namespaces[namespace] = prefix
|
||||||
|
# if allowed_attrs and (namespace, localpart) not in allowed_attrs:
|
||||||
|
# raise AttributeError, "Attribute %s:%s is not allowed in element <%s>" % ( prefix, localpart, self.tagName)
|
||||||
|
c = AttrConverters()
|
||||||
|
self.attributes[prefix + ":" + localpart] = c.convert((namespace, localpart), value, self.qname)
|
||||||
|
|
||||||
|
def getAttrNS(self, namespace, localpart):
|
||||||
|
prefix = _nsassign(namespace)
|
||||||
|
if not self.namespaces.has_key(namespace):
|
||||||
|
self.namespaces[namespace] = prefix
|
||||||
|
return self.attributes.get(prefix + ":" + localpart)
|
||||||
|
|
||||||
|
def removeAttrNS(self, namespace, localpart):
|
||||||
|
prefix = _nsassign(namespace)
|
||||||
|
if not self.namespaces.has_key(namespace):
|
||||||
|
self.namespaces[namespace] = prefix
|
||||||
|
del self.attributes[prefix + ":" + localpart]
|
||||||
|
|
||||||
|
def getAttribute(self, attr):
|
||||||
|
allowed_attrs = self.allowed_attributes()
|
||||||
|
if allowed_attrs is None:
|
||||||
|
if type(attr) == type(()):
|
||||||
|
prefix, localname = attr
|
||||||
|
return self.getAttrNS(prefix, localname)
|
||||||
|
else:
|
||||||
|
raise AttributeError, "Unable to get simple attribute - use (namespace, localpart)"
|
||||||
|
else:
|
||||||
|
# Construct a list of allowed arguments
|
||||||
|
allowed_args = [ a[1].lower().replace('-','') for a in allowed_attrs]
|
||||||
|
i = allowed_args.index(attr)
|
||||||
|
return self.getAttrNS(allowed_attrs[i][0], allowed_attrs[i][1])
|
||||||
|
|
||||||
|
def write_open_tag(self, level, f):
|
||||||
|
f.write('<'+self.tagName)
|
||||||
|
if level == 0:
|
||||||
|
for namespace, prefix in self.namespaces.items():
|
||||||
|
f.write(' xmlns:' + prefix + '="'+ _escape(str(namespace))+'"')
|
||||||
|
for attkey in self.attributes.keys():
|
||||||
|
f.write(' '+_escape(str(attkey))+'='+_quoteattr(unicode(self.attributes[attkey]).encode('utf-8')))
|
||||||
|
f.write('>')
|
||||||
|
|
||||||
|
def write_close_tag(self, level, f):
|
||||||
|
f.write('</'+self.tagName+'>')
|
||||||
|
|
||||||
|
def toXml(self, level, f):
|
||||||
|
""" Generate XML stream out of the tree structure """
|
||||||
|
f.write('<'+self.tagName)
|
||||||
|
if level == 0:
|
||||||
|
for namespace, prefix in self.namespaces.items():
|
||||||
|
f.write(' xmlns:' + prefix + '="'+ _escape(str(namespace))+'"')
|
||||||
|
for attkey in self.attributes.keys():
|
||||||
|
f.write(' '+_escape(str(attkey))+'='+_quoteattr(unicode(self.attributes[attkey]).encode('utf-8')))
|
||||||
|
if self.childNodes:
|
||||||
|
f.write('>')
|
||||||
|
for element in self.childNodes:
|
||||||
|
element.toXml(level+1,f)
|
||||||
|
f.write('</'+self.tagName+'>')
|
||||||
|
else:
|
||||||
|
f.write('/>')
|
||||||
|
|
||||||
|
def _getElementsByObj(self, obj, accumulator):
|
||||||
|
if self.qname == obj.qname:
|
||||||
|
accumulator.append(self)
|
||||||
|
for e in self.childNodes:
|
||||||
|
if e.nodeType == Node.ELEMENT_NODE:
|
||||||
|
accumulator = e._getElementsByObj(obj, accumulator)
|
||||||
|
return accumulator
|
||||||
|
|
||||||
|
def getElementsByType(self, element):
|
||||||
|
obj = element(check_grammar=False)
|
||||||
|
return self._getElementsByObj(obj,[])
|
||||||
|
|
330
src/odf/elementtypes.py
Normal file
330
src/odf/elementtypes.py
Normal file
@ -0,0 +1,330 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2008 Søren Roug, European Environment Agency
|
||||||
|
#
|
||||||
|
# This library is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU Lesser General Public
|
||||||
|
# License as published by the Free Software Foundation; either
|
||||||
|
# version 2.1 of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This library is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
# Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public
|
||||||
|
# License along with this library; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
#
|
||||||
|
# Contributor(s):
|
||||||
|
#
|
||||||
|
|
||||||
|
from namespaces import *
|
||||||
|
|
||||||
|
# Inline element don't cause a box
|
||||||
|
# They are analogous to the HTML elements SPAN, B, I etc.
|
||||||
|
inline_elements = (
|
||||||
|
(TEXTNS,u'a'),
|
||||||
|
(TEXTNS,u'author-initials'),
|
||||||
|
(TEXTNS,u'author-name'),
|
||||||
|
(TEXTNS,u'bibliography-mark'),
|
||||||
|
(TEXTNS,u'bookmark-ref'),
|
||||||
|
(TEXTNS,u'chapter'),
|
||||||
|
(TEXTNS,u'character-count'),
|
||||||
|
(TEXTNS,u'conditional-text'),
|
||||||
|
(TEXTNS,u'creation-date'),
|
||||||
|
(TEXTNS,u'creation-time'),
|
||||||
|
(TEXTNS,u'creator'),
|
||||||
|
(TEXTNS,u'database-display'),
|
||||||
|
(TEXTNS,u'database-name'),
|
||||||
|
(TEXTNS,u'database-next'),
|
||||||
|
(TEXTNS,u'database-row-number'),
|
||||||
|
(TEXTNS,u'database-row-select'),
|
||||||
|
(TEXTNS,u'date'),
|
||||||
|
(TEXTNS,u'dde-connection'),
|
||||||
|
(TEXTNS,u'description'),
|
||||||
|
(TEXTNS,u'editing-cycles'),
|
||||||
|
(TEXTNS,u'editing-duration'),
|
||||||
|
(TEXTNS,u'execute-macro'),
|
||||||
|
(TEXTNS,u'expression'),
|
||||||
|
(TEXTNS,u'file-name'),
|
||||||
|
(TEXTNS,u'hidden-paragraph'),
|
||||||
|
(TEXTNS,u'hidden-text'),
|
||||||
|
(TEXTNS,u'image-count'),
|
||||||
|
(TEXTNS,u'initial-creator'),
|
||||||
|
(TEXTNS,u'keywords'),
|
||||||
|
(TEXTNS,u'measure'),
|
||||||
|
(TEXTNS,u'modification-date'),
|
||||||
|
(TEXTNS,u'modification-time'),
|
||||||
|
(TEXTNS,u'note-ref'),
|
||||||
|
(TEXTNS,u'object-count'),
|
||||||
|
(TEXTNS,u'page-continuation'),
|
||||||
|
(TEXTNS,u'page-count'),
|
||||||
|
(TEXTNS,u'page-number'),
|
||||||
|
(TEXTNS,u'page-variable-get'),
|
||||||
|
(TEXTNS,u'page-variable-set'),
|
||||||
|
(TEXTNS,u'paragraph-count'),
|
||||||
|
(TEXTNS,u'placeholder'),
|
||||||
|
(TEXTNS,u'print-date'),
|
||||||
|
(TEXTNS,u'printed-by'),
|
||||||
|
(TEXTNS,u'print-time'),
|
||||||
|
(TEXTNS,u'reference-ref'),
|
||||||
|
(TEXTNS,u'ruby'),
|
||||||
|
(TEXTNS,u'ruby-base'),
|
||||||
|
(TEXTNS,u'ruby-text'),
|
||||||
|
(TEXTNS,u'script'),
|
||||||
|
(TEXTNS,u'sender-city'),
|
||||||
|
(TEXTNS,u'sender-company'),
|
||||||
|
(TEXTNS,u'sender-country'),
|
||||||
|
(TEXTNS,u'sender-email'),
|
||||||
|
(TEXTNS,u'sender-fax'),
|
||||||
|
(TEXTNS,u'sender-firstname'),
|
||||||
|
(TEXTNS,u'sender-initials'),
|
||||||
|
(TEXTNS,u'sender-lastname'),
|
||||||
|
(TEXTNS,u'sender-phone-private'),
|
||||||
|
(TEXTNS,u'sender-phone-work'),
|
||||||
|
(TEXTNS,u'sender-position'),
|
||||||
|
(TEXTNS,u'sender-postal-code'),
|
||||||
|
(TEXTNS,u'sender-state-or-province'),
|
||||||
|
(TEXTNS,u'sender-street'),
|
||||||
|
(TEXTNS,u'sender-title'),
|
||||||
|
(TEXTNS,u'sequence'),
|
||||||
|
(TEXTNS,u'sequence-ref'),
|
||||||
|
(TEXTNS,u'sheet-name'),
|
||||||
|
(TEXTNS,u'span'),
|
||||||
|
(TEXTNS,u'subject'),
|
||||||
|
(TEXTNS,u'table-count'),
|
||||||
|
(TEXTNS,u'table-formula'),
|
||||||
|
(TEXTNS,u'template-name'),
|
||||||
|
(TEXTNS,u'text-input'),
|
||||||
|
(TEXTNS,u'time'),
|
||||||
|
(TEXTNS,u'title'),
|
||||||
|
(TEXTNS,u'user-defined'),
|
||||||
|
(TEXTNS,u'user-field-get'),
|
||||||
|
(TEXTNS,u'user-field-input'),
|
||||||
|
(TEXTNS,u'variable-get'),
|
||||||
|
(TEXTNS,u'variable-input'),
|
||||||
|
(TEXTNS,u'variable-set'),
|
||||||
|
(TEXTNS,u'word-count'),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
struct_elements = (
|
||||||
|
(CONFIGNS,'config-item-set'),
|
||||||
|
(TABLENS,u'table-cell'),
|
||||||
|
)
|
||||||
|
|
||||||
|
# It is almost impossible to determine what elements are block elements.
|
||||||
|
# There are so many that don't fit the form
|
||||||
|
block_elements = (
|
||||||
|
(TEXTNS,u'h'),
|
||||||
|
(TEXTNS,u'p'),
|
||||||
|
(TEXTNS,u'list'),
|
||||||
|
(TEXTNS,u'list-item'),
|
||||||
|
(TEXTNS,u'section'),
|
||||||
|
)
|
||||||
|
|
||||||
|
declarative_elements = (
|
||||||
|
(OFFICENS,u'font-face-decls'),
|
||||||
|
(PRESENTATIONNS,u'date-time-decl'),
|
||||||
|
(PRESENTATIONNS,u'footer-decl'),
|
||||||
|
(PRESENTATIONNS,u'header-decl'),
|
||||||
|
(TABLENS,u'table-template'),
|
||||||
|
(TEXTNS,u'alphabetical-index-entry-template'),
|
||||||
|
(TEXTNS,u'alphabetical-index-source'),
|
||||||
|
(TEXTNS,u'bibliography-entry-template'),
|
||||||
|
(TEXTNS,u'bibliography-source'),
|
||||||
|
(TEXTNS,u'dde-connection-decls'),
|
||||||
|
(TEXTNS,u'illustration-index-entry-template'),
|
||||||
|
(TEXTNS,u'illustration-index-source'),
|
||||||
|
(TEXTNS,u'index-source-styles'),
|
||||||
|
(TEXTNS,u'index-title-template'),
|
||||||
|
(TEXTNS,u'note-continuation-notice-backward'),
|
||||||
|
(TEXTNS,u'note-continuation-notice-forward'),
|
||||||
|
(TEXTNS,u'notes-configuration'),
|
||||||
|
(TEXTNS,u'object-index-entry-template'),
|
||||||
|
(TEXTNS,u'object-index-source'),
|
||||||
|
(TEXTNS,u'sequence-decls'),
|
||||||
|
(TEXTNS,u'table-index-entry-template'),
|
||||||
|
(TEXTNS,u'table-index-source'),
|
||||||
|
(TEXTNS,u'table-of-content-entry-template'),
|
||||||
|
(TEXTNS,u'table-of-content-source'),
|
||||||
|
(TEXTNS,u'user-field-decls'),
|
||||||
|
(TEXTNS,u'user-index-entry-template'),
|
||||||
|
(TEXTNS,u'user-index-source'),
|
||||||
|
(TEXTNS,u'variable-decls'),
|
||||||
|
)
|
||||||
|
|
||||||
|
empty_elements = (
|
||||||
|
(ANIMNS,u'animate'),
|
||||||
|
(ANIMNS,u'animateColor'),
|
||||||
|
(ANIMNS,u'animateMotion'),
|
||||||
|
(ANIMNS,u'animateTransform'),
|
||||||
|
(ANIMNS,u'audio'),
|
||||||
|
(ANIMNS,u'param'),
|
||||||
|
(ANIMNS,u'set'),
|
||||||
|
(ANIMNS,u'transitionFilter'),
|
||||||
|
(CHARTNS,u'categories'),
|
||||||
|
(CHARTNS,u'data-point'),
|
||||||
|
(CHARTNS,u'domain'),
|
||||||
|
(CHARTNS,u'error-indicator'),
|
||||||
|
(CHARTNS,u'floor'),
|
||||||
|
(CHARTNS,u'grid'),
|
||||||
|
(CHARTNS,u'legend'),
|
||||||
|
(CHARTNS,u'mean-value'),
|
||||||
|
(CHARTNS,u'regression-curve'),
|
||||||
|
(CHARTNS,u'stock-gain-marker'),
|
||||||
|
(CHARTNS,u'stock-loss-marker'),
|
||||||
|
(CHARTNS,u'stock-range-line'),
|
||||||
|
(CHARTNS,u'symbol-image'),
|
||||||
|
(CHARTNS,u'wall'),
|
||||||
|
(DR3DNS,u'cube'),
|
||||||
|
(DR3DNS,u'extrude'),
|
||||||
|
(DR3DNS,u'light'),
|
||||||
|
(DR3DNS,u'rotate'),
|
||||||
|
(DR3DNS,u'sphere'),
|
||||||
|
(DRAWNS,u'contour-path'),
|
||||||
|
(DRAWNS,u'contour-polygon'),
|
||||||
|
(DRAWNS,u'equation'),
|
||||||
|
(DRAWNS,u'fill-image'),
|
||||||
|
(DRAWNS,u'floating-frame'),
|
||||||
|
(DRAWNS,u'glue-point'),
|
||||||
|
(DRAWNS,u'gradient'),
|
||||||
|
(DRAWNS,u'handle'),
|
||||||
|
(DRAWNS,u'hatch'),
|
||||||
|
(DRAWNS,u'layer'),
|
||||||
|
(DRAWNS,u'marker'),
|
||||||
|
(DRAWNS,u'opacity'),
|
||||||
|
(DRAWNS,u'page-thumbnail'),
|
||||||
|
(DRAWNS,u'param'),
|
||||||
|
(DRAWNS,u'stroke-dash'),
|
||||||
|
(FORMNS,u'connection-resource'),
|
||||||
|
(FORMNS,u'list-value'),
|
||||||
|
(FORMNS,u'property'),
|
||||||
|
(MANIFESTNS,u'algorithm'),
|
||||||
|
(MANIFESTNS,u'key-derivation'),
|
||||||
|
(METANS,u'auto-reload'),
|
||||||
|
(METANS,u'document-statistic'),
|
||||||
|
(METANS,u'hyperlink-behaviour'),
|
||||||
|
(METANS,u'template'),
|
||||||
|
(NUMBERNS,u'am-pm'),
|
||||||
|
(NUMBERNS,u'boolean'),
|
||||||
|
(NUMBERNS,u'day'),
|
||||||
|
(NUMBERNS,u'day-of-week'),
|
||||||
|
(NUMBERNS,u'era'),
|
||||||
|
(NUMBERNS,u'fraction'),
|
||||||
|
(NUMBERNS,u'hours'),
|
||||||
|
(NUMBERNS,u'minutes'),
|
||||||
|
(NUMBERNS,u'month'),
|
||||||
|
(NUMBERNS,u'quarter'),
|
||||||
|
(NUMBERNS,u'scientific-number'),
|
||||||
|
(NUMBERNS,u'seconds'),
|
||||||
|
(NUMBERNS,u'text-content'),
|
||||||
|
(NUMBERNS,u'week-of-year'),
|
||||||
|
(NUMBERNS,u'year'),
|
||||||
|
(OFFICENS,u'dde-source'),
|
||||||
|
(PRESENTATIONNS,u'date-time'),
|
||||||
|
(PRESENTATIONNS,u'footer'),
|
||||||
|
(PRESENTATIONNS,u'header'),
|
||||||
|
(PRESENTATIONNS,u'placeholder'),
|
||||||
|
(PRESENTATIONNS,u'play'),
|
||||||
|
(PRESENTATIONNS,u'show'),
|
||||||
|
(PRESENTATIONNS,u'sound'),
|
||||||
|
(SCRIPTNS,u'event-listener'),
|
||||||
|
(STYLENS,u'column'),
|
||||||
|
(STYLENS,u'column-sep'),
|
||||||
|
(STYLENS,u'drop-cap'),
|
||||||
|
(STYLENS,u'footnote-sep'),
|
||||||
|
(STYLENS,u'list-level-properties'),
|
||||||
|
(STYLENS,u'map'),
|
||||||
|
(STYLENS,u'ruby-properties'),
|
||||||
|
(STYLENS,u'table-column-properties'),
|
||||||
|
(STYLENS,u'tab-stop'),
|
||||||
|
(STYLENS,u'text-properties'),
|
||||||
|
(SVGNS,u'definition-src'),
|
||||||
|
(SVGNS,u'font-face-format'),
|
||||||
|
(SVGNS,u'font-face-name'),
|
||||||
|
(SVGNS,u'stop'),
|
||||||
|
(TABLENS,u'body'),
|
||||||
|
(TABLENS,u'cell-address'),
|
||||||
|
(TABLENS,u'cell-range-source'),
|
||||||
|
(TABLENS,u'change-deletion'),
|
||||||
|
(TABLENS,u'consolidation'),
|
||||||
|
(TABLENS,u'database-source-query'),
|
||||||
|
(TABLENS,u'database-source-sql'),
|
||||||
|
(TABLENS,u'database-source-table'),
|
||||||
|
(TABLENS,u'data-pilot-display-info'),
|
||||||
|
(TABLENS,u'data-pilot-field-reference'),
|
||||||
|
(TABLENS,u'data-pilot-group-member'),
|
||||||
|
(TABLENS,u'data-pilot-layout-info'),
|
||||||
|
(TABLENS,u'data-pilot-member'),
|
||||||
|
(TABLENS,u'data-pilot-sort-info'),
|
||||||
|
(TABLENS,u'data-pilot-subtotal'),
|
||||||
|
(TABLENS,u'dependency'),
|
||||||
|
(TABLENS,u'error-macro'),
|
||||||
|
(TABLENS,u'even-columns'),
|
||||||
|
(TABLENS,u'even-rows'),
|
||||||
|
(TABLENS,u'filter-condition'),
|
||||||
|
(TABLENS,u'first-column'),
|
||||||
|
(TABLENS,u'first-row'),
|
||||||
|
(TABLENS,u'highlighted-range'),
|
||||||
|
(TABLENS,u'insertion-cut-off'),
|
||||||
|
(TABLENS,u'iteration'),
|
||||||
|
(TABLENS,u'label-range'),
|
||||||
|
(TABLENS,u'last-column'),
|
||||||
|
(TABLENS,u'last-row'),
|
||||||
|
(TABLENS,u'movement-cut-off'),
|
||||||
|
(TABLENS,u'named-expression'),
|
||||||
|
(TABLENS,u'named-range'),
|
||||||
|
(TABLENS,u'null-date'),
|
||||||
|
(TABLENS,u'odd-columns'),
|
||||||
|
(TABLENS,u'odd-rows'),
|
||||||
|
(TABLENS,u'operation'),
|
||||||
|
(TABLENS,u'scenario'),
|
||||||
|
(TABLENS,u'sort-by'),
|
||||||
|
(TABLENS,u'sort-groups'),
|
||||||
|
(TABLENS,u'source-range-address'),
|
||||||
|
(TABLENS,u'source-service'),
|
||||||
|
(TABLENS,u'subtotal-field'),
|
||||||
|
(TABLENS,u'table-column'),
|
||||||
|
(TABLENS,u'table-source'),
|
||||||
|
(TABLENS,u'target-range-address'),
|
||||||
|
(TEXTNS,u'alphabetical-index-auto-mark-file'),
|
||||||
|
(TEXTNS,u'alphabetical-index-mark'),
|
||||||
|
(TEXTNS,u'alphabetical-index-mark-end'),
|
||||||
|
(TEXTNS,u'alphabetical-index-mark-start'),
|
||||||
|
(TEXTNS,u'bookmark'),
|
||||||
|
(TEXTNS,u'bookmark-end'),
|
||||||
|
(TEXTNS,u'bookmark-start'),
|
||||||
|
(TEXTNS,u'change'),
|
||||||
|
(TEXTNS,u'change-end'),
|
||||||
|
(TEXTNS,u'change-start'),
|
||||||
|
(TEXTNS,u'dde-connection-decl'),
|
||||||
|
(TEXTNS,u'index-entry-bibliography'),
|
||||||
|
(TEXTNS,u'index-entry-chapter'),
|
||||||
|
(TEXTNS,u'index-entry-link-end'),
|
||||||
|
(TEXTNS,u'index-entry-link-start'),
|
||||||
|
(TEXTNS,u'index-entry-page-number'),
|
||||||
|
(TEXTNS,u'index-entry-tab-stop'),
|
||||||
|
(TEXTNS,u'index-entry-text'),
|
||||||
|
(TEXTNS,u'index-source-style'),
|
||||||
|
(TEXTNS,u'line-break'),
|
||||||
|
(TEXTNS,u'page'),
|
||||||
|
(TEXTNS,u'reference-mark'),
|
||||||
|
(TEXTNS,u'reference-mark-end'),
|
||||||
|
(TEXTNS,u'reference-mark-start'),
|
||||||
|
(TEXTNS,u's'),
|
||||||
|
(TEXTNS,u'section-source'),
|
||||||
|
(TEXTNS,u'sequence-decl'),
|
||||||
|
(TEXTNS,u'soft-page-break'),
|
||||||
|
(TEXTNS,u'sort-key'),
|
||||||
|
(TEXTNS,u'tab'),
|
||||||
|
(TEXTNS,u'toc-mark'),
|
||||||
|
(TEXTNS,u'toc-mark-end'),
|
||||||
|
(TEXTNS,u'toc-mark-start'),
|
||||||
|
(TEXTNS,u'user-field-decl'),
|
||||||
|
(TEXTNS,u'user-index-mark'),
|
||||||
|
(TEXTNS,u'user-index-mark-end'),
|
||||||
|
(TEXTNS,u'user-index-mark-start'),
|
||||||
|
(TEXTNS,u'variable-decl')
|
||||||
|
)
|
115
src/odf/form.py
Normal file
115
src/odf/form.py
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
|
||||||
|
#
|
||||||
|
# This library is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU Lesser General Public
|
||||||
|
# License as published by the Free Software Foundation; either
|
||||||
|
# version 2.1 of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This library is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
# Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public
|
||||||
|
# License along with this library; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
#
|
||||||
|
# Contributor(s):
|
||||||
|
#
|
||||||
|
|
||||||
|
from namespaces import FORMNS
|
||||||
|
from element import Element
|
||||||
|
|
||||||
|
|
||||||
|
# Autogenerated
|
||||||
|
def Button(**args):
|
||||||
|
return Element(qname = (FORMNS,'button'), **args)
|
||||||
|
|
||||||
|
def Checkbox(**args):
|
||||||
|
return Element(qname = (FORMNS,'checkbox'), **args)
|
||||||
|
|
||||||
|
def Column(**args):
|
||||||
|
return Element(qname = (FORMNS,'column'), **args)
|
||||||
|
|
||||||
|
def Combobox(**args):
|
||||||
|
return Element(qname = (FORMNS,'combobox'), **args)
|
||||||
|
|
||||||
|
def ConnectionResource(**args):
|
||||||
|
return Element(qname = (FORMNS,'connection-resource'), **args)
|
||||||
|
|
||||||
|
def Date(**args):
|
||||||
|
return Element(qname = (FORMNS,'date'), **args)
|
||||||
|
|
||||||
|
def File(**args):
|
||||||
|
return Element(qname = (FORMNS,'file'), **args)
|
||||||
|
|
||||||
|
def FixedText(**args):
|
||||||
|
return Element(qname = (FORMNS,'fixed-text'), **args)
|
||||||
|
|
||||||
|
def Form(**args):
|
||||||
|
return Element(qname = (FORMNS,'form'), **args)
|
||||||
|
|
||||||
|
def FormattedText(**args):
|
||||||
|
return Element(qname = (FORMNS,'formatted-text'), **args)
|
||||||
|
|
||||||
|
def Frame(**args):
|
||||||
|
return Element(qname = (FORMNS,'frame'), **args)
|
||||||
|
|
||||||
|
def GenericControl(**args):
|
||||||
|
return Element(qname = (FORMNS,'generic-control'), **args)
|
||||||
|
|
||||||
|
def Grid(**args):
|
||||||
|
return Element(qname = (FORMNS,'grid'), **args)
|
||||||
|
|
||||||
|
def Hidden(**args):
|
||||||
|
return Element(qname = (FORMNS,'hidden'), **args)
|
||||||
|
|
||||||
|
def Image(**args):
|
||||||
|
return Element(qname = (FORMNS,'image'), **args)
|
||||||
|
|
||||||
|
def ImageFrame(**args):
|
||||||
|
return Element(qname = (FORMNS,'image-frame'), **args)
|
||||||
|
|
||||||
|
def Item(**args):
|
||||||
|
return Element(qname = (FORMNS,'item'), **args)
|
||||||
|
|
||||||
|
def ListProperty(**args):
|
||||||
|
return Element(qname = (FORMNS,'list-property'), **args)
|
||||||
|
|
||||||
|
def ListValue(**args):
|
||||||
|
return Element(qname = (FORMNS,'list-value'), **args)
|
||||||
|
|
||||||
|
def Listbox(**args):
|
||||||
|
return Element(qname = (FORMNS,'listbox'), **args)
|
||||||
|
|
||||||
|
def Number(**args):
|
||||||
|
return Element(qname = (FORMNS,'number'), **args)
|
||||||
|
|
||||||
|
def Option(**args):
|
||||||
|
return Element(qname = (FORMNS,'option'), **args)
|
||||||
|
|
||||||
|
def Password(**args):
|
||||||
|
return Element(qname = (FORMNS,'password'), **args)
|
||||||
|
|
||||||
|
def Properties(**args):
|
||||||
|
return Element(qname = (FORMNS,'properties'), **args)
|
||||||
|
|
||||||
|
def Property(**args):
|
||||||
|
return Element(qname = (FORMNS,'property'), **args)
|
||||||
|
|
||||||
|
def Radio(**args):
|
||||||
|
return Element(qname = (FORMNS,'radio'), **args)
|
||||||
|
|
||||||
|
def Text(**args):
|
||||||
|
return Element(qname = (FORMNS,'text'), **args)
|
||||||
|
|
||||||
|
def Textarea(**args):
|
||||||
|
return Element(qname = (FORMNS,'textarea'), **args)
|
||||||
|
|
||||||
|
def Time(**args):
|
||||||
|
return Element(qname = (FORMNS,'time'), **args)
|
||||||
|
|
||||||
|
def ValueRange(**args):
|
||||||
|
return Element(qname = (FORMNS,'value-range'), **args)
|
||||||
|
|
8150
src/odf/grammar.py
Normal file
8150
src/odf/grammar.py
Normal file
File diff suppressed because it is too large
Load Diff
117
src/odf/load.py
Normal file
117
src/odf/load.py
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2007-2008 Søren Roug, European Environment Agency
|
||||||
|
#
|
||||||
|
# This library is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU Lesser General Public
|
||||||
|
# License as published by the Free Software Foundation; either
|
||||||
|
# version 2.1 of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This library is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
# Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public
|
||||||
|
# License along with this library; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
#
|
||||||
|
# Contributor(s):
|
||||||
|
#
|
||||||
|
|
||||||
|
# This script is to be embedded in opendocument.py later
|
||||||
|
# The purpose is to read an ODT/ODP/ODS file and create the datastructure
|
||||||
|
# in memory. The user should then be able to make operations and then save
|
||||||
|
# the structure again.
|
||||||
|
|
||||||
|
from xml.sax import make_parser,handler
|
||||||
|
from xml.sax.xmlreader import InputSource
|
||||||
|
import xml.sax.saxutils
|
||||||
|
from element import Element
|
||||||
|
from namespaces import OFFICENS
|
||||||
|
|
||||||
|
try:
|
||||||
|
from cStringIO import StringIO
|
||||||
|
except ImportError:
|
||||||
|
from StringIO import StringIO
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Parse the XML files
|
||||||
|
#
|
||||||
|
class LoadParser(handler.ContentHandler):
|
||||||
|
""" Extract headings from content.xml of an ODT file """
|
||||||
|
triggers = (
|
||||||
|
(OFFICENS, 'automatic-styles'), (OFFICENS, 'body'),
|
||||||
|
(OFFICENS, 'font-face-decls'), (OFFICENS, 'master-styles'),
|
||||||
|
(OFFICENS, 'meta'), (OFFICENS, 'scripts'),
|
||||||
|
(OFFICENS, 'settings'), (OFFICENS, 'styles') )
|
||||||
|
|
||||||
|
def __init__(self, document):
|
||||||
|
self.doc = document
|
||||||
|
self.data = []
|
||||||
|
self.level = 0
|
||||||
|
self.parse = False
|
||||||
|
|
||||||
|
def characters(self, data):
|
||||||
|
if self.parse == False:
|
||||||
|
return
|
||||||
|
self.data.append(data)
|
||||||
|
|
||||||
|
def startElementNS(self, tag, qname, attrs):
|
||||||
|
if tag in self.triggers:
|
||||||
|
self.parse = True
|
||||||
|
if self.doc._parsing != "styles.xml" and tag == (OFFICENS, 'font-face-decls'):
|
||||||
|
self.parse = False
|
||||||
|
if self.parse == False:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.level = self.level + 1
|
||||||
|
# Add any accumulated text content
|
||||||
|
content = ''.join(self.data).strip()
|
||||||
|
if len(content) > 0:
|
||||||
|
self.parent.addText(content)
|
||||||
|
self.data = []
|
||||||
|
# Create the element
|
||||||
|
attrdict = {}
|
||||||
|
for (att,value) in attrs.items():
|
||||||
|
attrdict[att] = value
|
||||||
|
try:
|
||||||
|
e = Element(qname = tag, qattributes=attrdict, check_grammar=False)
|
||||||
|
self.curr = e
|
||||||
|
except AttributeError, v:
|
||||||
|
print "Error: %s" % v
|
||||||
|
|
||||||
|
if tag == (OFFICENS, 'automatic-styles'):
|
||||||
|
e = self.doc.automaticstyles
|
||||||
|
elif tag == (OFFICENS, 'body'):
|
||||||
|
e = self.doc.body
|
||||||
|
elif tag == (OFFICENS, 'master-styles'):
|
||||||
|
e = self.doc.masterstyles
|
||||||
|
elif tag == (OFFICENS, 'meta'):
|
||||||
|
e = self.doc.meta
|
||||||
|
elif tag == (OFFICENS,'scripts'):
|
||||||
|
e = self.doc.scripts
|
||||||
|
elif tag == (OFFICENS,'settings'):
|
||||||
|
e = self.doc.settings
|
||||||
|
elif tag == (OFFICENS,'styles'):
|
||||||
|
e = self.doc.styles
|
||||||
|
elif self.doc._parsing == "styles.xml" and tag == (OFFICENS, 'font-face-decls'):
|
||||||
|
e = self.doc.fontfacedecls
|
||||||
|
elif hasattr(self,'parent'):
|
||||||
|
self.parent.addElement(e, check_grammar=False)
|
||||||
|
self.parent = e
|
||||||
|
|
||||||
|
|
||||||
|
def endElementNS(self, tag, qname):
|
||||||
|
if self.parse == False:
|
||||||
|
return
|
||||||
|
self.level = self.level - 1
|
||||||
|
str = ''.join(self.data)
|
||||||
|
if len(str.strip()) > 0:
|
||||||
|
self.curr.addText(str)
|
||||||
|
self.data = []
|
||||||
|
self.curr = self.curr.parentNode
|
||||||
|
self.parent = self.curr
|
||||||
|
if tag in self.triggers:
|
||||||
|
self.parse = False
|
53
src/odf/manifest.py
Normal file
53
src/odf/manifest.py
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
|
||||||
|
#
|
||||||
|
# This library is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU Lesser General Public
|
||||||
|
# License as published by the Free Software Foundation; either
|
||||||
|
# version 2.1 of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This library is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
# Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public
|
||||||
|
# License along with this library; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
#
|
||||||
|
# Contributor(s):
|
||||||
|
#
|
||||||
|
#
|
||||||
|
|
||||||
|
from namespaces import MANIFESTNS
|
||||||
|
from element import Element
|
||||||
|
|
||||||
|
# Autogenerated
|
||||||
|
def Manifest(**args):
|
||||||
|
return Element(qname = (MANIFESTNS,'manifest'), **args)
|
||||||
|
|
||||||
|
def FileEntry(**args):
|
||||||
|
return Element(qname = (MANIFESTNS,'file-entry'), **args)
|
||||||
|
|
||||||
|
def EncryptionData(**args):
|
||||||
|
return Element(qname = (MANIFESTNS,'encryption-data'), **args)
|
||||||
|
|
||||||
|
def Algorithm(**args):
|
||||||
|
return Element(qname = (MANIFESTNS,'algorithm'), **args)
|
||||||
|
|
||||||
|
def KeyDerivation(**args):
|
||||||
|
return Element(qname = (MANIFESTNS,'key-derivation'), **args)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import cStringIO
|
||||||
|
xml=cStringIO.StringIO()
|
||||||
|
m = Manifest()
|
||||||
|
f = FileEntry(mediatype="text/xml", fullpath="content.xml")
|
||||||
|
m.addElement(f)
|
||||||
|
|
||||||
|
m.toXml(0,xml)
|
||||||
|
print xml.getvalue()
|
||||||
|
|
30
src/odf/math.py
Normal file
30
src/odf/math.py
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
|
||||||
|
#
|
||||||
|
# This library is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU Lesser General Public
|
||||||
|
# License as published by the Free Software Foundation; either
|
||||||
|
# version 2.1 of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This library is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
# Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public
|
||||||
|
# License along with this library; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
#
|
||||||
|
# Contributor(s):
|
||||||
|
#
|
||||||
|
|
||||||
|
from namespaces import MATHNS
|
||||||
|
from element import Element
|
||||||
|
|
||||||
|
# ODF 1.0 section 12.5
|
||||||
|
# Mathematical content is represented by MathML 2.0
|
||||||
|
|
||||||
|
# Autogenerated
|
||||||
|
def Math(**args):
|
||||||
|
return Element(qname = (MATHNS,'math'), **args)
|
||||||
|
|
66
src/odf/meta.py
Normal file
66
src/odf/meta.py
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
|
||||||
|
#
|
||||||
|
# This library is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU Lesser General Public
|
||||||
|
# License as published by the Free Software Foundation; either
|
||||||
|
# version 2.1 of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This library is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
# Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public
|
||||||
|
# License along with this library; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
#
|
||||||
|
# Contributor(s):
|
||||||
|
#
|
||||||
|
|
||||||
|
from namespaces import METANS
|
||||||
|
from element import Element
|
||||||
|
|
||||||
|
# Autogenerated
|
||||||
|
def AutoReload(**args):
|
||||||
|
return Element(qname = (METANS,'auto-reload'), **args)
|
||||||
|
|
||||||
|
def CreationDate(**args):
|
||||||
|
return Element(qname = (METANS,'creation-date'), **args)
|
||||||
|
|
||||||
|
def DateString(**args):
|
||||||
|
return Element(qname = (METANS,'date-string'), **args)
|
||||||
|
|
||||||
|
def DocumentStatistic(**args):
|
||||||
|
return Element(qname = (METANS,'document-statistic'), **args)
|
||||||
|
|
||||||
|
def EditingCycles(**args):
|
||||||
|
return Element(qname = (METANS,'editing-cycles'), **args)
|
||||||
|
|
||||||
|
def EditingDuration(**args):
|
||||||
|
return Element(qname = (METANS,'editing-duration'), **args)
|
||||||
|
|
||||||
|
def Generator(**args):
|
||||||
|
return Element(qname = (METANS,'generator'), **args)
|
||||||
|
|
||||||
|
def HyperlinkBehaviour(**args):
|
||||||
|
return Element(qname = (METANS,'hyperlink-behaviour'), **args)
|
||||||
|
|
||||||
|
def InitialCreator(**args):
|
||||||
|
return Element(qname = (METANS,'initial-creator'), **args)
|
||||||
|
|
||||||
|
def Keyword(**args):
|
||||||
|
return Element(qname = (METANS,'keyword'), **args)
|
||||||
|
|
||||||
|
def PrintDate(**args):
|
||||||
|
return Element(qname = (METANS,'print-date'), **args)
|
||||||
|
|
||||||
|
def PrintedBy(**args):
|
||||||
|
return Element(qname = (METANS,'printed-by'), **args)
|
||||||
|
|
||||||
|
def Template(**args):
|
||||||
|
return Element(qname = (METANS,'template'), **args)
|
||||||
|
|
||||||
|
def UserDefined(**args):
|
||||||
|
return Element(qname = (METANS,'user-defined'), **args)
|
||||||
|
|
81
src/odf/namespaces.py
Normal file
81
src/odf/namespaces.py
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
|
||||||
|
#
|
||||||
|
# This library is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU Lesser General Public
|
||||||
|
# License as published by the Free Software Foundation; either
|
||||||
|
# version 2.1 of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This library is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
# Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public
|
||||||
|
# License along with this library; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
#
|
||||||
|
# Contributor(s):
|
||||||
|
#
|
||||||
|
TOOLSVERSION = u"ODFPY/0.8.1dev"
|
||||||
|
|
||||||
|
ANIMNS = u"urn:oasis:names:tc:opendocument:xmlns:animation:1.0"
|
||||||
|
CHARTNS = u"urn:oasis:names:tc:opendocument:xmlns:chart:1.0"
|
||||||
|
CONFIGNS = u"urn:oasis:names:tc:opendocument:xmlns:config:1.0"
|
||||||
|
DBNS = u"http://openoffice.org/2004/database"
|
||||||
|
DCNS = u"http://purl.org/dc/elements/1.1/"
|
||||||
|
DOMNS = u"http://www.w3.org/2001/xml-events"
|
||||||
|
DR3DNS = u"urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0"
|
||||||
|
DRAWNS = u"urn:oasis:names:tc:opendocument:xmlns:drawing:1.0"
|
||||||
|
FONS = u"urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0"
|
||||||
|
FORMNS = u"urn:oasis:names:tc:opendocument:xmlns:form:1.0"
|
||||||
|
KOFFICENS = u"http://www.koffice.org/2005/"
|
||||||
|
MANIFESTNS = u"urn:oasis:names:tc:opendocument:xmlns:manifest:1.0"
|
||||||
|
MATHNS = u"http://www.w3.org/1998/Math/MathML"
|
||||||
|
METANS = u"urn:oasis:names:tc:opendocument:xmlns:meta:1.0"
|
||||||
|
NUMBERNS = u"urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0"
|
||||||
|
OFFICENS = u"urn:oasis:names:tc:opendocument:xmlns:office:1.0"
|
||||||
|
OOONS = u"http://openoffice.org/2004/office"
|
||||||
|
OOOWNS = u"http://openoffice.org/2004/writer"
|
||||||
|
OOOCNS = u"http://openoffice.org/2004/calc"
|
||||||
|
PRESENTATIONNS = u"urn:oasis:names:tc:opendocument:xmlns:presentation:1.0"
|
||||||
|
SCRIPTNS = u"urn:oasis:names:tc:opendocument:xmlns:script:1.0"
|
||||||
|
SMILNS = u"urn:oasis:names:tc:opendocument:xmlns:smil-compatible:1.0"
|
||||||
|
STYLENS = u"urn:oasis:names:tc:opendocument:xmlns:style:1.0"
|
||||||
|
SVGNS = u"urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0"
|
||||||
|
TABLENS = u"urn:oasis:names:tc:opendocument:xmlns:table:1.0"
|
||||||
|
TEXTNS = u"urn:oasis:names:tc:opendocument:xmlns:text:1.0"
|
||||||
|
XFORMSNS = u"http://www.w3.org/2002/xforms"
|
||||||
|
XLINKNS = u"http://www.w3.org/1999/xlink"
|
||||||
|
|
||||||
|
|
||||||
|
nsdict = {
|
||||||
|
ANIMNS: u'anim',
|
||||||
|
CHARTNS: u'chart',
|
||||||
|
CONFIGNS: u'config',
|
||||||
|
DBNS: u'db',
|
||||||
|
DCNS: u'dc',
|
||||||
|
DOMNS: u'dom',
|
||||||
|
DR3DNS: u'dr3d',
|
||||||
|
DRAWNS: u'draw',
|
||||||
|
FONS: u'fo',
|
||||||
|
FORMNS: u'form',
|
||||||
|
KOFFICENS: u'koffice',
|
||||||
|
MANIFESTNS: u'manifest',
|
||||||
|
MATHNS: u'math',
|
||||||
|
METANS: u'meta',
|
||||||
|
NUMBERNS: u'number',
|
||||||
|
OFFICENS: u'office',
|
||||||
|
OOONS: u'ooo',
|
||||||
|
OOOWNS: u'ooow',
|
||||||
|
OOOCNS: u'ooc',
|
||||||
|
PRESENTATIONNS: u'presentation',
|
||||||
|
SCRIPTNS: u'script',
|
||||||
|
SMILNS: u'smil',
|
||||||
|
STYLENS: u'style',
|
||||||
|
SVGNS: u'svg',
|
||||||
|
TABLENS: u'table',
|
||||||
|
TEXTNS: u'text',
|
||||||
|
XFORMSNS: u'xforms',
|
||||||
|
XLINKNS: u'xlink',
|
||||||
|
}
|
104
src/odf/number.py
Normal file
104
src/odf/number.py
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
|
||||||
|
#
|
||||||
|
# This library is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU Lesser General Public
|
||||||
|
# License as published by the Free Software Foundation; either
|
||||||
|
# version 2.1 of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This library is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
# Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public
|
||||||
|
# License along with this library; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
#
|
||||||
|
# Contributor(s):
|
||||||
|
#
|
||||||
|
|
||||||
|
from namespaces import NUMBERNS
|
||||||
|
from element import Element
|
||||||
|
from style import StyleElement
|
||||||
|
|
||||||
|
|
||||||
|
# Autogenerated
|
||||||
|
def AmPm(**args):
|
||||||
|
return Element(qname = (NUMBERNS,'am-pm'), **args)
|
||||||
|
|
||||||
|
def Boolean(**args):
|
||||||
|
return Element(qname = (NUMBERNS,'boolean'), **args)
|
||||||
|
|
||||||
|
def BooleanStyle(**args):
|
||||||
|
return StyleElement(qname = (NUMBERNS,'boolean-style'), **args)
|
||||||
|
|
||||||
|
def CurrencyStyle(**args):
|
||||||
|
return StyleElement(qname = (NUMBERNS,'currency-style'), **args)
|
||||||
|
|
||||||
|
def CurrencySymbol(**args):
|
||||||
|
return Element(qname = (NUMBERNS,'currency-symbol'), **args)
|
||||||
|
|
||||||
|
def DateStyle(**args):
|
||||||
|
return StyleElement(qname = (NUMBERNS,'date-style'), **args)
|
||||||
|
|
||||||
|
def Day(**args):
|
||||||
|
return Element(qname = (NUMBERNS,'day'), **args)
|
||||||
|
|
||||||
|
def DayOfWeek(**args):
|
||||||
|
return Element(qname = (NUMBERNS,'day-of-week'), **args)
|
||||||
|
|
||||||
|
def EmbeddedText(**args):
|
||||||
|
return Element(qname = (NUMBERNS,'embedded-text'), **args)
|
||||||
|
|
||||||
|
def Era(**args):
|
||||||
|
return Element(qname = (NUMBERNS,'era'), **args)
|
||||||
|
|
||||||
|
def Fraction(**args):
|
||||||
|
return Element(qname = (NUMBERNS,'fraction'), **args)
|
||||||
|
|
||||||
|
def Hours(**args):
|
||||||
|
return Element(qname = (NUMBERNS,'hours'), **args)
|
||||||
|
|
||||||
|
def Minutes(**args):
|
||||||
|
return Element(qname = (NUMBERNS,'minutes'), **args)
|
||||||
|
|
||||||
|
def Month(**args):
|
||||||
|
return Element(qname = (NUMBERNS,'month'), **args)
|
||||||
|
|
||||||
|
def Number(**args):
|
||||||
|
return Element(qname = (NUMBERNS,'number'), **args)
|
||||||
|
|
||||||
|
def NumberStyle(**args):
|
||||||
|
return StyleElement(qname = (NUMBERNS,'number-style'), **args)
|
||||||
|
|
||||||
|
def PercentageStyle(**args):
|
||||||
|
return StyleElement(qname = (NUMBERNS,'percentage-style'), **args)
|
||||||
|
|
||||||
|
def Quarter(**args):
|
||||||
|
return Element(qname = (NUMBERNS,'quarter'), **args)
|
||||||
|
|
||||||
|
def ScientificNumber(**args):
|
||||||
|
return Element(qname = (NUMBERNS,'scientific-number'), **args)
|
||||||
|
|
||||||
|
def Seconds(**args):
|
||||||
|
return Element(qname = (NUMBERNS,'seconds'), **args)
|
||||||
|
|
||||||
|
def Text(**args):
|
||||||
|
return Element(qname = (NUMBERNS,'text'), **args)
|
||||||
|
|
||||||
|
def TextContent(**args):
|
||||||
|
return Element(qname = (NUMBERNS,'text-content'), **args)
|
||||||
|
|
||||||
|
def TextStyle(**args):
|
||||||
|
return StyleElement(qname = (NUMBERNS,'text-style'), **args)
|
||||||
|
|
||||||
|
def TimeStyle(**args):
|
||||||
|
return StyleElement(qname = (NUMBERNS,'time-style'), **args)
|
||||||
|
|
||||||
|
def WeekOfYear(**args):
|
||||||
|
return Element(qname = (NUMBERNS,'week-of-year'), **args)
|
||||||
|
|
||||||
|
def Year(**args):
|
||||||
|
return Element(qname = (NUMBERNS,'year'), **args)
|
||||||
|
|
579
src/odf/odf2moinmoin.py
Normal file
579
src/odf/odf2moinmoin.py
Normal file
@ -0,0 +1,579 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2006-2008 Søren Roug, European Environment Agency
|
||||||
|
#
|
||||||
|
# This library is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU Lesser General Public
|
||||||
|
# License as published by the Free Software Foundation; either
|
||||||
|
# version 2.1 of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This library is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
# Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public
|
||||||
|
# License along with this library; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
#
|
||||||
|
# See http://trac.edgewall.org/wiki/WikiFormatting
|
||||||
|
#
|
||||||
|
# Contributor(s):
|
||||||
|
#
|
||||||
|
|
||||||
|
import sys, zipfile, xml.dom.minidom
|
||||||
|
from namespaces import nsdict
|
||||||
|
from elementtypes import *
|
||||||
|
|
||||||
|
IGNORED_TAGS = [
|
||||||
|
'draw:a'
|
||||||
|
'draw:g',
|
||||||
|
'draw:line',
|
||||||
|
'draw:object-ole',
|
||||||
|
'office:annotation',
|
||||||
|
'presentation:notes',
|
||||||
|
'svg:desc',
|
||||||
|
] + [ nsdict[item[0]]+":"+item[1] for item in empty_elements]
|
||||||
|
|
||||||
|
INLINE_TAGS = [ nsdict[item[0]]+":"+item[1] for item in inline_elements]
|
||||||
|
|
||||||
|
|
||||||
|
class TextProps:
|
||||||
|
""" Holds properties for a text style. """
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
|
||||||
|
self.italic = False
|
||||||
|
self.bold = False
|
||||||
|
self.fixed = False
|
||||||
|
self.underlined = False
|
||||||
|
self.strikethrough = False
|
||||||
|
self.superscript = False
|
||||||
|
self.subscript = False
|
||||||
|
|
||||||
|
def setItalic(self, value):
|
||||||
|
if value == "italic":
|
||||||
|
self.italic = True
|
||||||
|
elif value == "normal":
|
||||||
|
self.italic = False
|
||||||
|
|
||||||
|
def setBold(self, value):
|
||||||
|
if value == "bold":
|
||||||
|
self.bold = True
|
||||||
|
elif value == "normal":
|
||||||
|
self.bold = False
|
||||||
|
|
||||||
|
def setFixed(self, value):
|
||||||
|
self.fixed = value
|
||||||
|
|
||||||
|
def setUnderlined(self, value):
|
||||||
|
if value and value != "none":
|
||||||
|
self.underlined = True
|
||||||
|
|
||||||
|
def setStrikethrough(self, value):
|
||||||
|
if value and value != "none":
|
||||||
|
self.strikethrough = True
|
||||||
|
|
||||||
|
def setPosition(self, value):
|
||||||
|
if value is None or value == '':
|
||||||
|
return
|
||||||
|
posisize = value.split(' ')
|
||||||
|
textpos = posisize[0]
|
||||||
|
if textpos.find('%') == -1:
|
||||||
|
if textpos == "sub":
|
||||||
|
self.superscript = False
|
||||||
|
self.subscript = True
|
||||||
|
elif textpos == "super":
|
||||||
|
self.superscript = True
|
||||||
|
self.subscript = False
|
||||||
|
else:
|
||||||
|
itextpos = int(textpos[:textpos.find('%')])
|
||||||
|
if itextpos > 10:
|
||||||
|
self.superscript = False
|
||||||
|
self.subscript = True
|
||||||
|
elif itextpos < -10:
|
||||||
|
self.superscript = True
|
||||||
|
self.subscript = False
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
|
||||||
|
return "[italic=%s, bold=i%s, fixed=%s]" % (str(self.italic),
|
||||||
|
str(self.bold),
|
||||||
|
str(self.fixed))
|
||||||
|
|
||||||
|
class ParagraphProps:
|
||||||
|
""" Holds properties of a paragraph style. """
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
|
||||||
|
self.blockquote = False
|
||||||
|
self.headingLevel = 0
|
||||||
|
self.code = False
|
||||||
|
self.title = False
|
||||||
|
self.indented = 0
|
||||||
|
|
||||||
|
def setIndented(self, value):
|
||||||
|
self.indented = value
|
||||||
|
|
||||||
|
def setHeading(self, level):
|
||||||
|
self.headingLevel = level
|
||||||
|
|
||||||
|
def setTitle(self, value):
|
||||||
|
self.title = value
|
||||||
|
|
||||||
|
def setCode(self, value):
|
||||||
|
self.code = value
|
||||||
|
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
|
||||||
|
return "[bq=%s, h=%d, code=%s]" % (str(self.blockquote),
|
||||||
|
self.headingLevel,
|
||||||
|
str(self.code))
|
||||||
|
|
||||||
|
|
||||||
|
class ListProperties:
|
||||||
|
""" Holds properties for a list style. """
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.ordered = False
|
||||||
|
|
||||||
|
def setOrdered(self, value):
|
||||||
|
self.ordered = value
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class ODF2MoinMoin(object):
|
||||||
|
|
||||||
|
|
||||||
|
def __init__(self, filepath):
|
||||||
|
self.footnotes = []
|
||||||
|
self.footnoteCounter = 0
|
||||||
|
self.textStyles = {"Standard": TextProps()}
|
||||||
|
self.paragraphStyles = {"Standard": ParagraphProps()}
|
||||||
|
self.listStyles = {}
|
||||||
|
self.fixedFonts = []
|
||||||
|
self.hasTitle = 0
|
||||||
|
self.lastsegment = None
|
||||||
|
|
||||||
|
# Tags
|
||||||
|
self.elements = {
|
||||||
|
'draw:page': self.textToString,
|
||||||
|
'draw:frame': self.textToString,
|
||||||
|
'draw:image': self.draw_image,
|
||||||
|
'draw:text-box': self.textToString,
|
||||||
|
'text:a': self.text_a,
|
||||||
|
'text:note': self.text_note,
|
||||||
|
}
|
||||||
|
for tag in IGNORED_TAGS:
|
||||||
|
self.elements[tag] = self.do_nothing
|
||||||
|
|
||||||
|
for tag in INLINE_TAGS:
|
||||||
|
self.elements[tag] = self.inline_markup
|
||||||
|
self.elements['text:line-break'] = self.text_line_break
|
||||||
|
self.elements['text:s'] = self.text_s
|
||||||
|
self.elements['text:tab'] = self.text_tab
|
||||||
|
|
||||||
|
self.load(filepath)
|
||||||
|
|
||||||
|
def processFontDeclarations(self, fontDecl):
|
||||||
|
""" Extracts necessary font information from a font-declaration
|
||||||
|
element.
|
||||||
|
"""
|
||||||
|
for fontFace in fontDecl.getElementsByTagName("style:font-face"):
|
||||||
|
if fontFace.getAttribute("style:font-pitch") == "fixed":
|
||||||
|
self.fixedFonts.append(fontFace.getAttribute("style:name"))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def extractTextProperties(self, style, parent=None):
|
||||||
|
""" Extracts text properties from a style element. """
|
||||||
|
|
||||||
|
textProps = TextProps()
|
||||||
|
|
||||||
|
if parent:
|
||||||
|
parentProp = self.textStyles.get(parent, None)
|
||||||
|
if parentProp:
|
||||||
|
textProp = parentProp
|
||||||
|
|
||||||
|
textPropEl = style.getElementsByTagName("style:text-properties")
|
||||||
|
if not textPropEl: return textProps
|
||||||
|
|
||||||
|
textPropEl = textPropEl[0]
|
||||||
|
|
||||||
|
textProps.setItalic(textPropEl.getAttribute("fo:font-style"))
|
||||||
|
textProps.setBold(textPropEl.getAttribute("fo:font-weight"))
|
||||||
|
textProps.setUnderlined(textPropEl.getAttribute("style:text-underline-style"))
|
||||||
|
textProps.setStrikethrough(textPropEl.getAttribute("style:text-line-through-style"))
|
||||||
|
textProps.setPosition(textPropEl.getAttribute("style:text-position"))
|
||||||
|
|
||||||
|
if textPropEl.getAttribute("style:font-name") in self.fixedFonts:
|
||||||
|
textProps.setFixed(True)
|
||||||
|
|
||||||
|
return textProps
|
||||||
|
|
||||||
|
def extractParagraphProperties(self, style, parent=None):
|
||||||
|
""" Extracts paragraph properties from a style element. """
|
||||||
|
|
||||||
|
paraProps = ParagraphProps()
|
||||||
|
|
||||||
|
name = style.getAttribute("style:name")
|
||||||
|
|
||||||
|
if name.startswith("Heading_20_"):
|
||||||
|
level = name[11:]
|
||||||
|
try:
|
||||||
|
level = int(level)
|
||||||
|
paraProps.setHeading(level)
|
||||||
|
except:
|
||||||
|
level = 0
|
||||||
|
|
||||||
|
if name == "Title":
|
||||||
|
paraProps.setTitle(True)
|
||||||
|
|
||||||
|
paraPropEl = style.getElementsByTagName("style:paragraph-properties")
|
||||||
|
if paraPropEl:
|
||||||
|
paraPropEl = paraPropEl[0]
|
||||||
|
leftMargin = paraPropEl.getAttribute("fo:margin-left")
|
||||||
|
if leftMargin:
|
||||||
|
try:
|
||||||
|
leftMargin = float(leftMargin[:-2])
|
||||||
|
if leftMargin > 0.01:
|
||||||
|
paraProps.setIndented(True)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
textProps = self.extractTextProperties(style)
|
||||||
|
if textProps.fixed:
|
||||||
|
paraProps.setCode(True)
|
||||||
|
|
||||||
|
return paraProps
|
||||||
|
|
||||||
|
|
||||||
|
def processStyles(self, styleElements):
|
||||||
|
""" Runs through "style" elements extracting necessary information.
|
||||||
|
"""
|
||||||
|
|
||||||
|
for style in styleElements:
|
||||||
|
|
||||||
|
name = style.getAttribute("style:name")
|
||||||
|
|
||||||
|
if name == "Standard": continue
|
||||||
|
|
||||||
|
family = style.getAttribute("style:family")
|
||||||
|
parent = style.getAttribute("style:parent-style-name")
|
||||||
|
|
||||||
|
if family == "text":
|
||||||
|
self.textStyles[name] = self.extractTextProperties(style, parent)
|
||||||
|
|
||||||
|
elif family == "paragraph":
|
||||||
|
self.paragraphStyles[name] = \
|
||||||
|
self.extractParagraphProperties(style, parent)
|
||||||
|
self.textStyles[name] = self.extractTextProperties(style, parent)
|
||||||
|
|
||||||
|
def processListStyles(self, listStyleElements):
|
||||||
|
|
||||||
|
for style in listStyleElements:
|
||||||
|
name = style.getAttribute("style:name")
|
||||||
|
|
||||||
|
prop = ListProperties()
|
||||||
|
if style.hasChildNodes():
|
||||||
|
subitems = [el for el in style.childNodes
|
||||||
|
if el.nodeType == xml.dom.Node.ELEMENT_NODE
|
||||||
|
and el.tagName == "text:list-level-style-number"]
|
||||||
|
if len(subitems) > 0:
|
||||||
|
prop.setOrdered(True)
|
||||||
|
|
||||||
|
self.listStyles[name] = prop
|
||||||
|
|
||||||
|
|
||||||
|
def load(self, filepath):
|
||||||
|
""" Loads an ODT file. """
|
||||||
|
|
||||||
|
zip = zipfile.ZipFile(filepath)
|
||||||
|
|
||||||
|
styles_doc = xml.dom.minidom.parseString(zip.read("styles.xml"))
|
||||||
|
fontfacedecls = styles_doc.getElementsByTagName("office:font-face-decls")
|
||||||
|
if fontfacedecls:
|
||||||
|
self.processFontDeclarations(fontfacedecls[0])
|
||||||
|
self.processStyles(styles_doc.getElementsByTagName("style:style"))
|
||||||
|
self.processListStyles(styles_doc.getElementsByTagName("text:list-style"))
|
||||||
|
|
||||||
|
self.content = xml.dom.minidom.parseString(zip.read("content.xml"))
|
||||||
|
fontfacedecls = self.content.getElementsByTagName("office:font-face-decls")
|
||||||
|
if fontfacedecls:
|
||||||
|
self.processFontDeclarations(fontfacedecls[0])
|
||||||
|
|
||||||
|
self.processStyles(self.content.getElementsByTagName("style:style"))
|
||||||
|
self.processListStyles(self.content.getElementsByTagName("text:list-style"))
|
||||||
|
|
||||||
|
def compressCodeBlocks(self, text):
|
||||||
|
""" Removes extra blank lines from code blocks. """
|
||||||
|
|
||||||
|
return text
|
||||||
|
lines = text.split("\n")
|
||||||
|
buffer = []
|
||||||
|
numLines = len(lines)
|
||||||
|
for i in range(numLines):
|
||||||
|
|
||||||
|
if (lines[i].strip() or i == numLines-1 or i == 0 or
|
||||||
|
not ( lines[i-1].startswith(" ")
|
||||||
|
and lines[i+1].startswith(" ") ) ):
|
||||||
|
buffer.append("\n" + lines[i])
|
||||||
|
|
||||||
|
return ''.join(buffer)
|
||||||
|
|
||||||
|
#-----------------------------------
|
||||||
|
def do_nothing(self, node):
|
||||||
|
return ''
|
||||||
|
|
||||||
|
def draw_image(self, node):
|
||||||
|
"""
|
||||||
|
"""
|
||||||
|
|
||||||
|
link = node.getAttribute("xlink:href")
|
||||||
|
if link and link[:2] == './': # Indicates a sub-object, which isn't supported
|
||||||
|
return "%s\n" % link
|
||||||
|
if link and link[:9] == 'Pictures/':
|
||||||
|
link = link[9:]
|
||||||
|
return "[[Image(%s)]]\n" % link
|
||||||
|
|
||||||
|
def text_a(self, node):
|
||||||
|
text = self.textToString(node)
|
||||||
|
link = node.getAttribute("xlink:href")
|
||||||
|
if link.strip() == text.strip():
|
||||||
|
return "[%s] " % link.strip()
|
||||||
|
else:
|
||||||
|
return "[%s %s] " % (link.strip(), text.strip())
|
||||||
|
|
||||||
|
|
||||||
|
def text_line_break(self, node):
|
||||||
|
return "[[BR]]"
|
||||||
|
|
||||||
|
def text_note(self, node):
|
||||||
|
cite = (node.getElementsByTagName("text:note-citation")[0]
|
||||||
|
.childNodes[0].nodeValue)
|
||||||
|
body = (node.getElementsByTagName("text:note-body")[0]
|
||||||
|
.childNodes[0])
|
||||||
|
self.footnotes.append((cite, self.textToString(body)))
|
||||||
|
return "^%s^" % cite
|
||||||
|
|
||||||
|
def text_s(self, node):
|
||||||
|
try:
|
||||||
|
num = int(node.getAttribute("text:c"))
|
||||||
|
return " "*num
|
||||||
|
except:
|
||||||
|
return " "
|
||||||
|
|
||||||
|
def text_tab(self, node):
|
||||||
|
return " "
|
||||||
|
|
||||||
|
def inline_markup(self, node):
|
||||||
|
text = self.textToString(node)
|
||||||
|
|
||||||
|
if not text.strip():
|
||||||
|
return '' # don't apply styles to white space
|
||||||
|
|
||||||
|
styleName = node.getAttribute("text:style-name")
|
||||||
|
style = self.textStyles.get(styleName, TextProps())
|
||||||
|
|
||||||
|
if style.fixed:
|
||||||
|
return "`" + text + "`"
|
||||||
|
|
||||||
|
mark = []
|
||||||
|
if style:
|
||||||
|
if style.italic:
|
||||||
|
mark.append("''")
|
||||||
|
if style.bold:
|
||||||
|
mark.append("'''")
|
||||||
|
if style.underlined:
|
||||||
|
mark.append("__")
|
||||||
|
if style.strikethrough:
|
||||||
|
mark.append("~~")
|
||||||
|
if style.superscript:
|
||||||
|
mark.append("^")
|
||||||
|
if style.subscript:
|
||||||
|
mark.append(",,")
|
||||||
|
revmark = mark[:]
|
||||||
|
revmark.reverse()
|
||||||
|
return "%s%s%s" % (''.join(mark), text, ''.join(revmark))
|
||||||
|
|
||||||
|
#-----------------------------------
|
||||||
|
def listToString(self, listElement, indent = 0):
|
||||||
|
|
||||||
|
self.lastsegment = listElement.tagName
|
||||||
|
buffer = []
|
||||||
|
|
||||||
|
styleName = listElement.getAttribute("text:style-name")
|
||||||
|
props = self.listStyles.get(styleName, ListProperties())
|
||||||
|
|
||||||
|
i = 0
|
||||||
|
for item in listElement.childNodes:
|
||||||
|
buffer.append(" "*indent)
|
||||||
|
i += 1
|
||||||
|
if props.ordered:
|
||||||
|
number = str(i)
|
||||||
|
number = " " + number + ". "
|
||||||
|
buffer.append(" 1. ")
|
||||||
|
else:
|
||||||
|
buffer.append(" * ")
|
||||||
|
subitems = [el for el in item.childNodes
|
||||||
|
if el.tagName in ["text:p", "text:h", "text:list"]]
|
||||||
|
for subitem in subitems:
|
||||||
|
if subitem.tagName == "text:list":
|
||||||
|
buffer.append("\n")
|
||||||
|
buffer.append(self.listToString(subitem, indent+3))
|
||||||
|
else:
|
||||||
|
buffer.append(self.paragraphToString(subitem, indent+3))
|
||||||
|
self.lastsegment = subitem.tagName
|
||||||
|
self.lastsegment = item.tagName
|
||||||
|
buffer.append("\n")
|
||||||
|
|
||||||
|
return ''.join(buffer)
|
||||||
|
|
||||||
|
def tableToString(self, tableElement):
|
||||||
|
""" MoinMoin uses || to delimit table cells
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.lastsegment = tableElement.tagName
|
||||||
|
buffer = []
|
||||||
|
|
||||||
|
for item in tableElement.childNodes:
|
||||||
|
self.lastsegment = item.tagName
|
||||||
|
if item.tagName == "table:table-header-rows":
|
||||||
|
buffer.append(self.tableToString(item))
|
||||||
|
if item.tagName == "table:table-row":
|
||||||
|
buffer.append("\n||")
|
||||||
|
for cell in item.childNodes:
|
||||||
|
buffer.append(self.inline_markup(cell))
|
||||||
|
buffer.append("||")
|
||||||
|
self.lastsegment = cell.tagName
|
||||||
|
return ''.join(buffer)
|
||||||
|
|
||||||
|
|
||||||
|
def toString(self):
|
||||||
|
""" Converts the document to a string.
|
||||||
|
FIXME: Result from second call differs from first call
|
||||||
|
"""
|
||||||
|
body = self.content.getElementsByTagName("office:body")[0]
|
||||||
|
text = body.childNodes[0]
|
||||||
|
|
||||||
|
buffer = []
|
||||||
|
|
||||||
|
paragraphs = [el for el in text.childNodes
|
||||||
|
if el.tagName in ["draw:page", "text:p", "text:h","text:section",
|
||||||
|
"text:list", "table:table"]]
|
||||||
|
|
||||||
|
for paragraph in paragraphs:
|
||||||
|
if paragraph.tagName == "text:list":
|
||||||
|
text = self.listToString(paragraph)
|
||||||
|
elif paragraph.tagName == "text:section":
|
||||||
|
text = self.textToString(paragraph)
|
||||||
|
elif paragraph.tagName == "table:table":
|
||||||
|
text = self.tableToString(paragraph)
|
||||||
|
else:
|
||||||
|
text = self.paragraphToString(paragraph)
|
||||||
|
if text:
|
||||||
|
buffer.append(text)
|
||||||
|
|
||||||
|
if self.footnotes:
|
||||||
|
|
||||||
|
buffer.append("----")
|
||||||
|
for cite, body in self.footnotes:
|
||||||
|
buffer.append("%s: %s" % (cite, body))
|
||||||
|
|
||||||
|
|
||||||
|
buffer.append("")
|
||||||
|
return self.compressCodeBlocks('\n'.join(buffer))
|
||||||
|
|
||||||
|
|
||||||
|
def textToString(self, element):
|
||||||
|
|
||||||
|
buffer = []
|
||||||
|
|
||||||
|
for node in element.childNodes:
|
||||||
|
|
||||||
|
if node.nodeType == xml.dom.Node.TEXT_NODE:
|
||||||
|
buffer.append(node.nodeValue)
|
||||||
|
|
||||||
|
elif node.nodeType == xml.dom.Node.ELEMENT_NODE:
|
||||||
|
tag = node.tagName
|
||||||
|
|
||||||
|
if tag in ("draw:text-box", "draw:frame"):
|
||||||
|
buffer.append(self.textToString(node))
|
||||||
|
|
||||||
|
elif tag in ("text:p", "text:h"):
|
||||||
|
text = self.paragraphToString(node)
|
||||||
|
if text:
|
||||||
|
buffer.append(text)
|
||||||
|
elif tag == "text:list":
|
||||||
|
buffer.append(self.listToString(node))
|
||||||
|
else:
|
||||||
|
method = self.elements.get(tag)
|
||||||
|
if method:
|
||||||
|
buffer.append(method(node))
|
||||||
|
else:
|
||||||
|
buffer.append(" {" + tag + "} ")
|
||||||
|
|
||||||
|
return ''.join(buffer)
|
||||||
|
|
||||||
|
def paragraphToString(self, paragraph, indent = 0):
|
||||||
|
|
||||||
|
dummyParaProps = ParagraphProps()
|
||||||
|
|
||||||
|
style_name = paragraph.getAttribute("text:style-name")
|
||||||
|
paraProps = self.paragraphStyles.get(style_name, dummyParaProps)
|
||||||
|
text = self.inline_markup(paragraph)
|
||||||
|
|
||||||
|
if paraProps and not paraProps.code:
|
||||||
|
text = text.strip()
|
||||||
|
|
||||||
|
if paragraph.tagName == "text:p" and self.lastsegment == "text:p":
|
||||||
|
text = "\n" + text
|
||||||
|
|
||||||
|
self.lastsegment = paragraph.tagName
|
||||||
|
|
||||||
|
if paraProps.title:
|
||||||
|
self.hasTitle = 1
|
||||||
|
return "= " + text + " =\n"
|
||||||
|
|
||||||
|
outlinelevel = paragraph.getAttribute("text:outline-level")
|
||||||
|
if outlinelevel:
|
||||||
|
|
||||||
|
level = int(outlinelevel)
|
||||||
|
if self.hasTitle: level += 1
|
||||||
|
|
||||||
|
if level >= 1:
|
||||||
|
return "=" * level + " " + text + " " + "=" * level + "\n"
|
||||||
|
|
||||||
|
elif paraProps.code:
|
||||||
|
return "{{{\n" + text + "\n}}}\n"
|
||||||
|
|
||||||
|
if paraProps.indented:
|
||||||
|
return self.wrapParagraph(text, indent = indent, blockquote = True)
|
||||||
|
|
||||||
|
else:
|
||||||
|
return self.wrapParagraph(text, indent = indent)
|
||||||
|
|
||||||
|
|
||||||
|
def wrapParagraph(self, text, indent = 0, blockquote=False):
|
||||||
|
|
||||||
|
counter = 0
|
||||||
|
buffer = []
|
||||||
|
LIMIT = 50
|
||||||
|
|
||||||
|
if blockquote:
|
||||||
|
buffer.append(" ")
|
||||||
|
|
||||||
|
return ''.join(buffer) + text
|
||||||
|
# Unused from here
|
||||||
|
for token in text.split():
|
||||||
|
|
||||||
|
if counter > LIMIT - indent:
|
||||||
|
buffer.append("\n" + " "*indent)
|
||||||
|
if blockquote:
|
||||||
|
buffer.append(" ")
|
||||||
|
counter = 0
|
||||||
|
|
||||||
|
buffer.append(token + " ")
|
||||||
|
counter += len(token)
|
||||||
|
|
||||||
|
return ''.join(buffer)
|
1264
src/odf/odf2xhtml.py
Normal file
1264
src/odf/odf2xhtml.py
Normal file
File diff suppressed because it is too large
Load Diff
120
src/odf/odfmanifest.py
Normal file
120
src/odf/odfmanifest.py
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
|
||||||
|
#
|
||||||
|
# This library is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU Lesser General Public
|
||||||
|
# License as published by the Free Software Foundation; either
|
||||||
|
# version 2.1 of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This library is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
# Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public
|
||||||
|
# License along with this library; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
#
|
||||||
|
# Contributor(s):
|
||||||
|
#
|
||||||
|
|
||||||
|
# This script lists the content of the manifest.xml file
|
||||||
|
import zipfile
|
||||||
|
from xml.sax import make_parser,handler
|
||||||
|
from xml.sax.xmlreader import InputSource
|
||||||
|
import xml.sax.saxutils
|
||||||
|
|
||||||
|
try:
|
||||||
|
from cStringIO import StringIO
|
||||||
|
except ImportError:
|
||||||
|
from StringIO import StringIO
|
||||||
|
|
||||||
|
|
||||||
|
MANIFESTNS="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0"
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# ODFMANIFESTHANDLER
|
||||||
|
#
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class ODFManifestHandler(handler.ContentHandler):
|
||||||
|
""" The ODFManifestHandler parses a manifest file and produces a list of
|
||||||
|
content """
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.manifest = {}
|
||||||
|
|
||||||
|
# Tags
|
||||||
|
# FIXME: Also handle encryption data
|
||||||
|
self.elements = {
|
||||||
|
(MANIFESTNS, 'file-entry'): (self.s_file_entry, self.donothing),
|
||||||
|
}
|
||||||
|
|
||||||
|
def handle_starttag(self, tag, method, attrs):
|
||||||
|
method(tag,attrs)
|
||||||
|
|
||||||
|
def handle_endtag(self, tag, method):
|
||||||
|
method(tag)
|
||||||
|
|
||||||
|
def startElementNS(self, tag, qname, attrs):
|
||||||
|
method = self.elements.get(tag, (None, None))[0]
|
||||||
|
if method:
|
||||||
|
self.handle_starttag(tag, method, attrs)
|
||||||
|
else:
|
||||||
|
self.unknown_starttag(tag,attrs)
|
||||||
|
|
||||||
|
def endElementNS(self, tag, qname):
|
||||||
|
method = self.elements.get(tag, (None, None))[1]
|
||||||
|
if method:
|
||||||
|
self.handle_endtag(tag, method)
|
||||||
|
else:
|
||||||
|
self.unknown_endtag(tag)
|
||||||
|
|
||||||
|
def unknown_starttag(self, tag, attrs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def unknown_endtag(self, tag):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def donothing(self, tag, attrs=None):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def s_file_entry(self, tag, attrs):
|
||||||
|
m = attrs.get((MANIFESTNS, 'media-type'),"application/octet-stream")
|
||||||
|
p = attrs.get((MANIFESTNS, 'full-path'))
|
||||||
|
self.manifest[p] = { 'media-type':m, 'full-path':p }
|
||||||
|
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# Reading the file
|
||||||
|
#
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def manifestlist(manifestxml):
|
||||||
|
odhandler = ODFManifestHandler()
|
||||||
|
parser = make_parser()
|
||||||
|
parser.setFeature(handler.feature_namespaces, 1)
|
||||||
|
parser.setContentHandler(odhandler)
|
||||||
|
parser.setErrorHandler(handler.ErrorHandler())
|
||||||
|
|
||||||
|
inpsrc = InputSource()
|
||||||
|
inpsrc.setByteStream(StringIO(manifestxml))
|
||||||
|
parser.parse(inpsrc)
|
||||||
|
|
||||||
|
return odhandler.manifest
|
||||||
|
|
||||||
|
def odfmanifest(odtfile):
|
||||||
|
z = zipfile.ZipFile(odtfile)
|
||||||
|
manifest = z.read('META-INF/manifest.xml')
|
||||||
|
z.close()
|
||||||
|
return manifestlist(manifest)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import sys
|
||||||
|
result = odfmanifest(sys.argv[1])
|
||||||
|
for file in result.values():
|
||||||
|
print "%-40s %-40s" % (file['media-type'], file['full-path'])
|
||||||
|
|
104
src/odf/office.py
Normal file
104
src/odf/office.py
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
|
||||||
|
#
|
||||||
|
# This library is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU Lesser General Public
|
||||||
|
# License as published by the Free Software Foundation; either
|
||||||
|
# version 2.1 of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This library is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
# Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public
|
||||||
|
# License along with this library; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
#
|
||||||
|
# Contributor(s):
|
||||||
|
#
|
||||||
|
|
||||||
|
from namespaces import OFFICENS
|
||||||
|
from element import Element
|
||||||
|
from draw import StyleRefElement
|
||||||
|
|
||||||
|
# Autogenerated
|
||||||
|
def Annotation(**args):
|
||||||
|
return StyleRefElement(qname = (OFFICENS,'annotation'), **args)
|
||||||
|
|
||||||
|
def AutomaticStyles(**args):
|
||||||
|
return Element(qname = (OFFICENS, 'automatic-styles'), **args)
|
||||||
|
|
||||||
|
def BinaryData(**args):
|
||||||
|
return Element(qname = (OFFICENS,'binary-data'), **args)
|
||||||
|
|
||||||
|
def Body(**args):
|
||||||
|
return Element(qname = (OFFICENS, 'body'), **args)
|
||||||
|
|
||||||
|
def ChangeInfo(**args):
|
||||||
|
return Element(qname = (OFFICENS,'change-info'), **args)
|
||||||
|
|
||||||
|
def Chart(**args):
|
||||||
|
return Element(qname = (OFFICENS,'chart'), **args)
|
||||||
|
|
||||||
|
def DdeSource(**args):
|
||||||
|
return Element(qname = (OFFICENS,'dde-source'), **args)
|
||||||
|
|
||||||
|
def Document(version="1.0", **args):
|
||||||
|
return Element(qname = (OFFICENS,'document'), version=version, **args)
|
||||||
|
|
||||||
|
def DocumentContent(version="1.0", **args):
|
||||||
|
return Element(qname = (OFFICENS, 'document-content'), version=version, **args)
|
||||||
|
|
||||||
|
def DocumentMeta(version="1.0", **args):
|
||||||
|
return Element(qname = (OFFICENS, 'document-meta'), version=version, **args)
|
||||||
|
|
||||||
|
def DocumentSettings(version="1.0", **args):
|
||||||
|
return Element(qname = (OFFICENS, 'document-settings'), version=version, **args)
|
||||||
|
|
||||||
|
def DocumentStyles(version="1.0", **args):
|
||||||
|
return Element(qname = (OFFICENS, 'document-styles'), version=version, **args)
|
||||||
|
|
||||||
|
def Drawing(**args):
|
||||||
|
return Element(qname = (OFFICENS,'drawing'), **args)
|
||||||
|
|
||||||
|
def EventListeners(**args):
|
||||||
|
return Element(qname = (OFFICENS,'event-listeners'), **args)
|
||||||
|
|
||||||
|
def FontFaceDecls(**args):
|
||||||
|
return Element(qname = (OFFICENS, 'font-face-decls'), **args)
|
||||||
|
|
||||||
|
def Forms(**args):
|
||||||
|
return Element(qname = (OFFICENS,'forms'), **args)
|
||||||
|
|
||||||
|
def Image(**args):
|
||||||
|
return Element(qname = (OFFICENS,'image'), **args)
|
||||||
|
|
||||||
|
def MasterStyles(**args):
|
||||||
|
return Element(qname = (OFFICENS, 'master-styles'), **args)
|
||||||
|
|
||||||
|
def Meta(**args):
|
||||||
|
return Element(qname = (OFFICENS, 'meta'), **args)
|
||||||
|
|
||||||
|
def Presentation(**args):
|
||||||
|
return Element(qname = (OFFICENS,'presentation'), **args)
|
||||||
|
|
||||||
|
def Script(**args):
|
||||||
|
return Element(qname = (OFFICENS, 'script'), **args)
|
||||||
|
|
||||||
|
def Scripts(**args):
|
||||||
|
return Element(qname = (OFFICENS, 'scripts'), **args)
|
||||||
|
|
||||||
|
def Settings(**args):
|
||||||
|
return Element(qname = (OFFICENS, 'settings'), **args)
|
||||||
|
|
||||||
|
def Spreadsheet(**args):
|
||||||
|
return Element(qname = (OFFICENS, 'spreadsheet'), **args)
|
||||||
|
|
||||||
|
def Styles(**args):
|
||||||
|
return Element(qname = (OFFICENS, 'styles'), **args)
|
||||||
|
|
||||||
|
def Text(**args):
|
||||||
|
return Element(qname = (OFFICENS, 'text'), **args)
|
||||||
|
|
||||||
|
# Autogenerated end
|
71
src/odf/ooostyles.py
Normal file
71
src/odf/ooostyles.py
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
from style import Style, ParagraphProperties, TextProperties
|
||||||
|
|
||||||
|
def addOOoStandardStyles(styles):
|
||||||
|
style = Style(name="Standard", family="paragraph", attributes={'class':"text"})
|
||||||
|
styles.addElement(style)
|
||||||
|
|
||||||
|
style = Style(name="Text_20_body", displayname="Text body", family="paragraph", parentstylename="Standard", attributes={'class':"text"})
|
||||||
|
p = ParagraphProperties(margintop="0cm", marginbottom="0.212cm")
|
||||||
|
style.addElement(p)
|
||||||
|
styles.addElement(style)
|
||||||
|
|
||||||
|
style = Style(name="Text_20_body_20_indent", displayname="Text body indent", family="paragraph", parentstylename="Text_20_body", attributes={'class':"text"})
|
||||||
|
p = ParagraphProperties(marginleft="0.499cm", marginright="0cm", textindent="0cm", autotextindent="false")
|
||||||
|
style.addElement(p)
|
||||||
|
styles.addElement(style)
|
||||||
|
|
||||||
|
style = Style(name="Salutation", family="paragraph", parentstylename="Standard", attributes={'class':"text"})
|
||||||
|
p = ParagraphProperties(numberlines="false", linenumber=0)
|
||||||
|
style.addElement(p)
|
||||||
|
styles.addElement(style)
|
||||||
|
|
||||||
|
style = Style(name="Signature", family="paragraph", parentstylename="Standard", attributes={'class':"text"})
|
||||||
|
p = ParagraphProperties(numberlines="false", linenumber=0)
|
||||||
|
style.addElement(p)
|
||||||
|
styles.addElement(style)
|
||||||
|
|
||||||
|
style = Style(name="Heading", family="paragraph", parentstylename="Standard", nextstylename="Text_20_body", attributes={'class':"text"})
|
||||||
|
p = ParagraphProperties(margintop="0.423cm", marginbottom="0.212cm", keepwithnext="always")
|
||||||
|
style.addElement(p)
|
||||||
|
p = TextProperties(fontname="Nimbus Sans L", fontsize="14pt", fontnameasian="DejaVu LGC Sans", fontsizeasian="14pt", fontnamecomplex="DejaVu LGC Sans", fontsizecomplex="14pt")
|
||||||
|
style.addElement(p)
|
||||||
|
styles.addElement(style)
|
||||||
|
|
||||||
|
style = Style(name="Heading_20_1", displayname="Heading 1", family="paragraph", parentstylename="Heading", nextstylename="Text_20_body", attributes={'class':"text"}, defaultoutlinelevel=1)
|
||||||
|
p = TextProperties(fontsize="115%", fontweight="bold", fontsizeasian="115%", fontweightasian="bold", fontsizecomplex="115%", fontweightcomplex="bold")
|
||||||
|
style.addElement(p)
|
||||||
|
styles.addElement(style)
|
||||||
|
|
||||||
|
style = Style(name="Heading_20_2", displayname="Heading 2", family="paragraph", parentstylename="Heading", nextstylename="Text_20_body", attributes={'class':"text"}, defaultoutlinelevel=2)
|
||||||
|
p = TextProperties(fontsize="14pt", fontstyle="italic", fontweight="bold", fontsizeasian="14pt", fontstyleasian="italic", fontweightasian="bold", fontsizecomplex="14pt", fontstylecomplex="italic", fontweightcomplex="bold")
|
||||||
|
style.addElement(p)
|
||||||
|
styles.addElement(style)
|
||||||
|
|
||||||
|
style = Style(name="Heading_20_3", displayname="Heading 3", family="paragraph", parentstylename="Heading", nextstylename="Text_20_body", attributes={'class':"text"}, defaultoutlinelevel=3)
|
||||||
|
p = TextProperties(fontsize="14pt", fontweight="bold", fontsizeasian="14pt", fontweightasian="bold", fontsizecomplex="14pt", fontweightcomplex="bold")
|
||||||
|
style.addElement(p)
|
||||||
|
styles.addElement(style)
|
||||||
|
|
||||||
|
style = Style(name="List", family="paragraph", parentstylename="Text_20_body", attributes={'class':"list"})
|
||||||
|
styles.addElement(style)
|
||||||
|
|
||||||
|
style = Style(name="Caption", family="paragraph", parentstylename="Standard", attributes={'class':"extra"})
|
||||||
|
p = ParagraphProperties(margintop="0.212cm", marginbottom="0.212cm", numberlines="false", linenumber="0")
|
||||||
|
style.addElement(p)
|
||||||
|
p = TextProperties(fontsize="12pt", fontstyle="italic", fontsizeasian="12pt", fontstyleasian="italic", fontsizecomplex="12pt", fontstylecomplex="italic")
|
||||||
|
style.addElement(p)
|
||||||
|
styles.addElement(style)
|
||||||
|
|
||||||
|
style = Style(name="Index", family="paragraph", parentstylename="Standard", attributes={'class':"index"})
|
||||||
|
p = ParagraphProperties(numberlines="false", linenumber=0)
|
||||||
|
styles.addElement(style)
|
||||||
|
|
||||||
|
style = Style(name="Source_20_Text", displayname="Source Text", family="text")
|
||||||
|
p = TextProperties(fontname="Courier", fontnameasian="Courier", fontnamecomplex="Courier")
|
||||||
|
style.addElement(p)
|
||||||
|
styles.addElement(style)
|
||||||
|
|
||||||
|
style = Style(name="Variable", family="text")
|
||||||
|
p = TextProperties(fontstyle="italic", fontstyleasian="italic", fontstylecomplex="italic")
|
||||||
|
style.addElement(p)
|
||||||
|
styles.addElement(style)
|
558
src/odf/opendocument.py
Normal file
558
src/odf/opendocument.py
Normal file
@ -0,0 +1,558 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2006-2008 Søren Roug, European Environment Agency
|
||||||
|
#
|
||||||
|
# This library is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU Lesser General Public
|
||||||
|
# License as published by the Free Software Foundation; either
|
||||||
|
# version 2.1 of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This library is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
# Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public
|
||||||
|
# License along with this library; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
#
|
||||||
|
# Contributor(s):
|
||||||
|
#
|
||||||
|
|
||||||
|
__doc__="""Use OpenDocument to generate your documents."""
|
||||||
|
|
||||||
|
import zipfile, time, sys, mimetypes, copy
|
||||||
|
from cStringIO import StringIO
|
||||||
|
from namespaces import *
|
||||||
|
import manifest, meta
|
||||||
|
from office import *
|
||||||
|
import element
|
||||||
|
from attrconverters import make_NCName
|
||||||
|
from xml.sax.xmlreader import InputSource
|
||||||
|
from odfmanifest import manifestlist
|
||||||
|
|
||||||
|
__version__= TOOLSVERSION
|
||||||
|
|
||||||
|
_XMLPROLOGUE = u"<?xml version='1.0' encoding='UTF-8'?>\n"
|
||||||
|
|
||||||
|
UNIXPERMS = 0100644 << 16L # -rw-r--r--
|
||||||
|
|
||||||
|
IS_FILENAME = 0
|
||||||
|
IS_IMAGE = 1
|
||||||
|
# We need at least Python 2.2
|
||||||
|
assert sys.version_info[0]>=2 and sys.version_info[1] >= 2
|
||||||
|
|
||||||
|
sys.setrecursionlimit=50
|
||||||
|
#The recursion limit is set conservative so mistakes like
|
||||||
|
# s=content() s.addElement(s) won't eat up too much processor time.
|
||||||
|
|
||||||
|
odmimetypes = {
|
||||||
|
'application/vnd.oasis.opendocument.text': '.odt',
|
||||||
|
'application/vnd.oasis.opendocument.text-template': '.ott',
|
||||||
|
'application/vnd.oasis.opendocument.graphics': '.odg',
|
||||||
|
'application/vnd.oasis.opendocument.graphics-template': '.otg',
|
||||||
|
'application/vnd.oasis.opendocument.presentation': '.odp',
|
||||||
|
'application/vnd.oasis.opendocument.presentation-template': '.otp',
|
||||||
|
'application/vnd.oasis.opendocument.spreadsheet': '.ods',
|
||||||
|
'application/vnd.oasis.opendocument.spreadsheet-template': '.ots',
|
||||||
|
'application/vnd.oasis.opendocument.chart': '.odc',
|
||||||
|
'application/vnd.oasis.opendocument.chart-template': '.otc',
|
||||||
|
'application/vnd.oasis.opendocument.image': '.odi',
|
||||||
|
'application/vnd.oasis.opendocument.image-template': '.oti',
|
||||||
|
'application/vnd.oasis.opendocument.formula': '.odf',
|
||||||
|
'application/vnd.oasis.opendocument.formula-template': '.otf',
|
||||||
|
'application/vnd.oasis.opendocument.text-master': '.odm',
|
||||||
|
'application/vnd.oasis.opendocument.text-web': '.oth',
|
||||||
|
}
|
||||||
|
|
||||||
|
class OpenDocument:
|
||||||
|
""" A class to hold the content of an OpenDocument document
|
||||||
|
Use the xml method to write the XML
|
||||||
|
source to the screen or to a file
|
||||||
|
d = OpenDocument(mimetype)
|
||||||
|
fd.write(d.xml())
|
||||||
|
"""
|
||||||
|
thumbnail = None
|
||||||
|
|
||||||
|
def __init__(self, mimetype, add_generator=True):
|
||||||
|
self.mimetype = mimetype
|
||||||
|
self.childobjects = []
|
||||||
|
self.folder = "" # Always empty for toplevel documents
|
||||||
|
self.topnode = Document(mimetype=self.mimetype)
|
||||||
|
self.topnode.ownerDocument = self
|
||||||
|
|
||||||
|
self.clear_caches()
|
||||||
|
|
||||||
|
self.Pictures = {}
|
||||||
|
self.meta = Meta()
|
||||||
|
self.topnode.addElement(self.meta)
|
||||||
|
if add_generator:
|
||||||
|
self.meta.addElement(meta.Generator(text=TOOLSVERSION))
|
||||||
|
self.scripts = Scripts()
|
||||||
|
self.topnode.addElement(self.scripts)
|
||||||
|
self.fontfacedecls = FontFaceDecls()
|
||||||
|
self.topnode.addElement(self.fontfacedecls)
|
||||||
|
self.settings = Settings()
|
||||||
|
self.topnode.addElement(self.settings)
|
||||||
|
self.styles = Styles()
|
||||||
|
self.topnode.addElement(self.styles)
|
||||||
|
self.automaticstyles = AutomaticStyles()
|
||||||
|
self.topnode.addElement(self.automaticstyles)
|
||||||
|
self.masterstyles = MasterStyles()
|
||||||
|
self.topnode.addElement(self.masterstyles)
|
||||||
|
self.body = Body()
|
||||||
|
self.topnode.addElement(self.body)
|
||||||
|
|
||||||
|
def rebuild_caches(self, node=None):
|
||||||
|
if node is None: node = self.topnode
|
||||||
|
self.build_caches(node)
|
||||||
|
for e in node.childNodes:
|
||||||
|
if e.nodeType == element.Node.ELEMENT_NODE:
|
||||||
|
self.rebuild_caches(e)
|
||||||
|
|
||||||
|
def clear_caches(self):
|
||||||
|
self.element_dict = {}
|
||||||
|
self._styles_dict = {}
|
||||||
|
self._styles_ooo_fix = {}
|
||||||
|
|
||||||
|
def build_caches(self, element):
|
||||||
|
""" Called from element.py
|
||||||
|
"""
|
||||||
|
if not self.element_dict.has_key(element.qname):
|
||||||
|
self.element_dict[element.qname] = []
|
||||||
|
self.element_dict[element.qname].append(element)
|
||||||
|
if element.qname == (STYLENS, u'style'):
|
||||||
|
self._register_stylename(element) # Add to style dictionary
|
||||||
|
styleref = element.getAttrNS(TEXTNS,u'style-name')
|
||||||
|
if styleref is not None and self._styles_ooo_fix.has_key(styleref):
|
||||||
|
element.setAttrNS(TEXTNS,u'style-name', self._styles_ooo_fix[styleref])
|
||||||
|
|
||||||
|
def _register_stylename(self, element):
|
||||||
|
''' Register a style. But there are three style dictionaries:
|
||||||
|
office:styles, office:automatic-styles and office:master-styles
|
||||||
|
Chapter 14
|
||||||
|
'''
|
||||||
|
name = element.getAttrNS(STYLENS, u'name')
|
||||||
|
if name is None:
|
||||||
|
return
|
||||||
|
if element.parentNode.qname in ((OFFICENS,u'styles'), (OFFICENS,u'automatic-styles')):
|
||||||
|
if self._styles_dict.has_key(name):
|
||||||
|
newname = 'M'+name # Rename style
|
||||||
|
self._styles_ooo_fix[name] = newname
|
||||||
|
# From here on all references to the old name will refer to the new one
|
||||||
|
name = newname
|
||||||
|
element.setAttrNS(STYLENS, u'name', name)
|
||||||
|
self._styles_dict[name] = element
|
||||||
|
|
||||||
|
def toXml(self, filename=''):
|
||||||
|
xml=StringIO()
|
||||||
|
xml.write(_XMLPROLOGUE)
|
||||||
|
self.body.toXml(0, xml)
|
||||||
|
if not filename:
|
||||||
|
return xml.getvalue()
|
||||||
|
else:
|
||||||
|
f=file(filename,'w')
|
||||||
|
f.write(xml.getvalue())
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
def xml(self):
|
||||||
|
""" Generates the full document as an XML file
|
||||||
|
Always written as a bytestream in UTF-8 encoding
|
||||||
|
"""
|
||||||
|
self._replaceGenerator()
|
||||||
|
xml=StringIO()
|
||||||
|
xml.write(_XMLPROLOGUE)
|
||||||
|
self.topnode.toXml(0, xml)
|
||||||
|
return xml.getvalue()
|
||||||
|
|
||||||
|
|
||||||
|
def contentxml(self):
|
||||||
|
""" Generates the content.xml file
|
||||||
|
Always written as a bytestream in UTF-8 encoding
|
||||||
|
"""
|
||||||
|
xml=StringIO()
|
||||||
|
xml.write(_XMLPROLOGUE)
|
||||||
|
x = DocumentContent()
|
||||||
|
x.write_open_tag(0, xml)
|
||||||
|
if self.scripts.hasChildNodes():
|
||||||
|
self.scripts.toXml(1, xml)
|
||||||
|
if self.fontfacedecls.hasChildNodes():
|
||||||
|
self.fontfacedecls.toXml(1, xml)
|
||||||
|
a = AutomaticStyles()
|
||||||
|
stylelist = self._used_auto_styles([self.styles, self.body])
|
||||||
|
if len(stylelist) > 0:
|
||||||
|
a.write_open_tag(1, xml)
|
||||||
|
for s in stylelist:
|
||||||
|
s.toXml(2, xml)
|
||||||
|
a.write_close_tag(1, xml)
|
||||||
|
else:
|
||||||
|
a.toXml(1, xml)
|
||||||
|
self.body.toXml(1, xml)
|
||||||
|
x.write_close_tag(0, xml)
|
||||||
|
return xml.getvalue()
|
||||||
|
|
||||||
|
def manifestxml(self):
|
||||||
|
""" Generates the manifest.xml file """
|
||||||
|
xml=StringIO()
|
||||||
|
xml.write(_XMLPROLOGUE)
|
||||||
|
self.manifest.toXml(0,xml)
|
||||||
|
return xml.getvalue()
|
||||||
|
|
||||||
|
def metaxml(self):
|
||||||
|
""" Generates the meta.xml file """
|
||||||
|
self._replaceGenerator()
|
||||||
|
x = DocumentMeta()
|
||||||
|
x.addElement(self.meta)
|
||||||
|
xml=StringIO()
|
||||||
|
xml.write(_XMLPROLOGUE)
|
||||||
|
x.toXml(0,xml)
|
||||||
|
return xml.getvalue()
|
||||||
|
|
||||||
|
def settingsxml(self):
|
||||||
|
""" Generates the settings.xml file """
|
||||||
|
x = DocumentSettings()
|
||||||
|
x.addElement(self.settings)
|
||||||
|
xml=StringIO()
|
||||||
|
xml.write(_XMLPROLOGUE)
|
||||||
|
x.toXml(0,xml)
|
||||||
|
return xml.getvalue()
|
||||||
|
|
||||||
|
def _parseoneelement(self, top, stylenamelist):
|
||||||
|
""" Finds references to style objects in master-styles
|
||||||
|
and add the style name to the style list if not already there.
|
||||||
|
Recursive
|
||||||
|
"""
|
||||||
|
for e in top.childNodes:
|
||||||
|
if e.nodeType == element.Node.ELEMENT_NODE:
|
||||||
|
for styleref in ( (DRAWNS,u'style-name'),
|
||||||
|
(DRAWNS,u'text-style-name'),
|
||||||
|
(PRESENTATIONNS,u'style-name'),
|
||||||
|
(STYLENS,u'style-name'),
|
||||||
|
(STYLENS,u'list-style-name'),
|
||||||
|
(STYLENS,u'page-layout-name'),
|
||||||
|
(TABLENS,u'style-name'),
|
||||||
|
(TEXTNS,u'style-name') ):
|
||||||
|
if e.getAttrNS(styleref[0],styleref[1]):
|
||||||
|
stylename = e.getAttrNS(styleref[0],styleref[1])
|
||||||
|
if stylename not in stylenamelist:
|
||||||
|
stylenamelist.append(stylename)
|
||||||
|
stylenamelist = self._parseoneelement(e, stylenamelist)
|
||||||
|
return stylenamelist
|
||||||
|
|
||||||
|
def _used_auto_styles(self, segments):
|
||||||
|
""" Loop through the masterstyles elements, and find the automatic
|
||||||
|
styles that are used. These will be added to the automatic-styles
|
||||||
|
element in styles.xml
|
||||||
|
"""
|
||||||
|
stylenamelist = []
|
||||||
|
for top in segments:
|
||||||
|
stylenamelist = self._parseoneelement(top, stylenamelist)
|
||||||
|
stylelist = []
|
||||||
|
for e in self.automaticstyles.childNodes:
|
||||||
|
if e.getAttrNS(STYLENS,u'name') in stylenamelist:
|
||||||
|
stylelist.append(e)
|
||||||
|
return stylelist
|
||||||
|
|
||||||
|
def stylesxml(self):
|
||||||
|
""" Generates the styles.xml file """
|
||||||
|
xml=StringIO()
|
||||||
|
xml.write(_XMLPROLOGUE)
|
||||||
|
x = DocumentStyles()
|
||||||
|
x.write_open_tag(0, xml)
|
||||||
|
if self.fontfacedecls.hasChildNodes():
|
||||||
|
self.fontfacedecls.toXml(1, xml)
|
||||||
|
self.styles.toXml(1, xml)
|
||||||
|
a = AutomaticStyles()
|
||||||
|
a.write_open_tag(1, xml)
|
||||||
|
for s in self._used_auto_styles([self.masterstyles]):
|
||||||
|
s.toXml(2, xml)
|
||||||
|
a.write_close_tag(1, xml)
|
||||||
|
if self.masterstyles.hasChildNodes():
|
||||||
|
self.masterstyles.toXml(1, xml)
|
||||||
|
x.write_close_tag(0, xml)
|
||||||
|
return xml.getvalue()
|
||||||
|
|
||||||
|
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 subdirectory 'Pictures'
|
||||||
|
If passed a file ptr, mediatype must be set
|
||||||
|
"""
|
||||||
|
if content is None:
|
||||||
|
if mediatype is None:
|
||||||
|
mediatype, encoding = mimetypes.guess_type(filename)
|
||||||
|
if mediatype is None:
|
||||||
|
mediatype = ''
|
||||||
|
try: ext = filename[filename.rindex('.'):]
|
||||||
|
except: ext=''
|
||||||
|
else:
|
||||||
|
ext = mimetypes.guess_extension(mediatype)
|
||||||
|
manifestfn = "Pictures/%0.0f%s" % ((time.time()*10000000000), ext)
|
||||||
|
self.Pictures[manifestfn] = (IS_FILENAME, fileobj, mediatype)
|
||||||
|
else:
|
||||||
|
manifestfn = filename
|
||||||
|
self.Pictures[manifestfn] = (IS_IMAGE, content, mediatype)
|
||||||
|
return manifestfn
|
||||||
|
|
||||||
|
def addThumbnail(self, filecontent=None):
|
||||||
|
""" Add a fixed thumbnail
|
||||||
|
The thumbnail in the library is big, so this is pretty useless.
|
||||||
|
"""
|
||||||
|
if filecontent is None:
|
||||||
|
import thumbnail
|
||||||
|
self.thumbnail = thumbnail.thumbnail()
|
||||||
|
else:
|
||||||
|
self.thumbnail = filecontent
|
||||||
|
|
||||||
|
def addObject(self, document):
|
||||||
|
""" Add an object. The object must be an OpenDocument class
|
||||||
|
The return value will be the folder in the zipfile the object is stored in
|
||||||
|
"""
|
||||||
|
self.childobjects.append(document)
|
||||||
|
document.folder = "%s/Object %d" % (self.folder, len(self.childobjects))
|
||||||
|
return ".%s" % document.folder
|
||||||
|
|
||||||
|
def _savePictures(self, object, folder):
|
||||||
|
hasPictures = False
|
||||||
|
for arcname, picturerec in object.Pictures.items():
|
||||||
|
what_it_is, fileobj, mediatype = picturerec
|
||||||
|
self.manifest.addElement(manifest.FileEntry(fullpath="%s%s" % ( folder ,arcname), mediatype=mediatype))
|
||||||
|
hasPictures = True
|
||||||
|
if what_it_is == IS_FILENAME:
|
||||||
|
self._z.write(fileobj, arcname, zipfile.ZIP_STORED)
|
||||||
|
else:
|
||||||
|
zi = zipfile.ZipInfo(str(arcname), self._now)
|
||||||
|
zi.compress_type = zipfile.ZIP_STORED
|
||||||
|
zi.external_attr = UNIXPERMS
|
||||||
|
self._z.writestr(zi, fileobj)
|
||||||
|
if hasPictures:
|
||||||
|
self.manifest.addElement(manifest.FileEntry(fullpath="%sPictures/" % folder,mediatype=""))
|
||||||
|
# Look in subobjects
|
||||||
|
subobjectnum = 1
|
||||||
|
for subobject in object.childobjects:
|
||||||
|
self._savePictures(subobject,'%sObject %d/' % (folder, subobjectnum))
|
||||||
|
subobjectnum += 1
|
||||||
|
|
||||||
|
def _replaceGenerator(self):
|
||||||
|
""" Section 3.1.1: The application MUST NOT export the original identifier
|
||||||
|
belonging to the application that created the document.
|
||||||
|
"""
|
||||||
|
for m in self.meta.childNodes[:]:
|
||||||
|
if m.qname == (METANS, u'generator'):
|
||||||
|
self.meta.removeChild(m)
|
||||||
|
self.meta.addElement(meta.Generator(text=TOOLSVERSION))
|
||||||
|
|
||||||
|
def save(self, outputfile, addsuffix=False):
|
||||||
|
""" Save the document under the filename """
|
||||||
|
if outputfile == '-':
|
||||||
|
outputfp = zipfile.ZipFile(sys.stdout,"w")
|
||||||
|
else:
|
||||||
|
if addsuffix:
|
||||||
|
outputfile = outputfile + odmimetypes.get(self.mimetype,'.xxx')
|
||||||
|
outputfp = zipfile.ZipFile(outputfile,"w")
|
||||||
|
self._zipwrite(outputfp)
|
||||||
|
outputfp.close()
|
||||||
|
|
||||||
|
def write(self, outputfp):
|
||||||
|
zipoutputfp = zipfile.ZipFile(outputfp,"w")
|
||||||
|
self._zipwrite(zipoutputfp)
|
||||||
|
|
||||||
|
def _zipwrite(self, outputfp):
|
||||||
|
""" Write the document to an open file pointer """
|
||||||
|
self._z = outputfp
|
||||||
|
self._now = time.localtime()[:6]
|
||||||
|
self.manifest = manifest.Manifest()
|
||||||
|
|
||||||
|
# Write mimetype
|
||||||
|
zi = zipfile.ZipInfo('mimetype', self._now)
|
||||||
|
zi.compress_type = zipfile.ZIP_STORED
|
||||||
|
zi.external_attr = UNIXPERMS
|
||||||
|
self._z.writestr(zi, self.mimetype)
|
||||||
|
|
||||||
|
self._saveXmlObjects(self,"")
|
||||||
|
|
||||||
|
# Write pictures
|
||||||
|
self._savePictures(self,"")
|
||||||
|
|
||||||
|
# Write the thumbnail
|
||||||
|
if self.thumbnail is not None:
|
||||||
|
self.manifest.addElement(manifest.FileEntry(fullpath="Thumbnails/", mediatype=''))
|
||||||
|
self.manifest.addElement(manifest.FileEntry(fullpath="Thumbnails/thumbnail.png", mediatype=''))
|
||||||
|
zi = zipfile.ZipInfo("Thumbnails/thumbnail.png", self._now)
|
||||||
|
zi.compress_type = zipfile.ZIP_DEFLATED
|
||||||
|
zi.external_attr = UNIXPERMS
|
||||||
|
self._z.writestr(zi, self.thumbnail)
|
||||||
|
|
||||||
|
# Write manifest
|
||||||
|
zi = zipfile.ZipInfo("META-INF/manifest.xml", self._now)
|
||||||
|
zi.compress_type = zipfile.ZIP_DEFLATED
|
||||||
|
zi.external_attr = UNIXPERMS
|
||||||
|
self._z.writestr(zi, self.manifestxml() )
|
||||||
|
del self._z
|
||||||
|
del self._now
|
||||||
|
del self.manifest
|
||||||
|
|
||||||
|
|
||||||
|
def _saveXmlObjects(self, object, folder):
|
||||||
|
if self == object:
|
||||||
|
self.manifest.addElement(manifest.FileEntry(fullpath="/", mediatype=object.mimetype))
|
||||||
|
else:
|
||||||
|
self.manifest.addElement(manifest.FileEntry(fullpath=folder, mediatype=object.mimetype))
|
||||||
|
# Write styles
|
||||||
|
self.manifest.addElement(manifest.FileEntry(fullpath="%sstyles.xml" % folder, mediatype="text/xml"))
|
||||||
|
zi = zipfile.ZipInfo("%sstyles.xml" % folder, self._now)
|
||||||
|
zi.compress_type = zipfile.ZIP_DEFLATED
|
||||||
|
zi.external_attr = UNIXPERMS
|
||||||
|
self._z.writestr(zi, object.stylesxml() )
|
||||||
|
|
||||||
|
# Write content
|
||||||
|
self.manifest.addElement(manifest.FileEntry(fullpath="%scontent.xml" % folder, mediatype="text/xml"))
|
||||||
|
zi = zipfile.ZipInfo("%scontent.xml" % folder, self._now)
|
||||||
|
zi.compress_type = zipfile.ZIP_DEFLATED
|
||||||
|
zi.external_attr = UNIXPERMS
|
||||||
|
self._z.writestr(zi, object.contentxml() )
|
||||||
|
|
||||||
|
# Write settings
|
||||||
|
if self == object and self.settings.hasChildNodes():
|
||||||
|
self.manifest.addElement(manifest.FileEntry(fullpath="settings.xml",mediatype="text/xml"))
|
||||||
|
zi = zipfile.ZipInfo("%ssettings.xml" % folder, self._now)
|
||||||
|
zi.compress_type = zipfile.ZIP_DEFLATED
|
||||||
|
zi.external_attr = UNIXPERMS
|
||||||
|
self._z.writestr(zi, object.settingsxml() )
|
||||||
|
|
||||||
|
# Write meta
|
||||||
|
if self == object:
|
||||||
|
self.manifest.addElement(manifest.FileEntry(fullpath="meta.xml",mediatype="text/xml"))
|
||||||
|
zi = zipfile.ZipInfo("meta.xml", self._now)
|
||||||
|
zi.compress_type = zipfile.ZIP_DEFLATED
|
||||||
|
zi.external_attr = UNIXPERMS
|
||||||
|
self._z.writestr(zi, object.metaxml() )
|
||||||
|
|
||||||
|
# Write subobjects
|
||||||
|
subobjectnum = 1
|
||||||
|
for subobject in object.childobjects:
|
||||||
|
self._saveXmlObjects(subobject, '%sObject %d/' % (folder, subobjectnum))
|
||||||
|
subobjectnum += 1
|
||||||
|
|
||||||
|
# Document's DOM methods
|
||||||
|
def createElement(self, element):
|
||||||
|
""" Inconvenient interface to create an element, but follows XML-DOM.
|
||||||
|
Does not allow attributes as argument, therefore can't check grammar.
|
||||||
|
"""
|
||||||
|
return element(check_grammar=False)
|
||||||
|
|
||||||
|
def createTextNode(self, data):
|
||||||
|
""" Method to create a text node """
|
||||||
|
return element.Text(data)
|
||||||
|
|
||||||
|
def createCDATASection(self, data):
|
||||||
|
return element.CDATASection(cdata)
|
||||||
|
|
||||||
|
def getMediaType(self):
|
||||||
|
""" Returns the media type """
|
||||||
|
return self.mimetype
|
||||||
|
|
||||||
|
def getStyleByName(self, name):
|
||||||
|
ncname = make_NCName(name)
|
||||||
|
if self._styles_dict == {}:
|
||||||
|
self.rebuild_caches()
|
||||||
|
return self._styles_dict.get(ncname, None)
|
||||||
|
|
||||||
|
def getElementsByType(self, element):
|
||||||
|
obj = element(check_grammar=False)
|
||||||
|
if self.element_dict == {}:
|
||||||
|
self.rebuild_caches()
|
||||||
|
return self.element_dict.get(obj.qname, [])
|
||||||
|
|
||||||
|
# Convenience functions
|
||||||
|
def OpenDocumentChart():
|
||||||
|
doc = OpenDocument('application/vnd.oasis.opendocument.chart')
|
||||||
|
doc.chart = Chart()
|
||||||
|
doc.body.addElement(doc.chart)
|
||||||
|
return doc
|
||||||
|
|
||||||
|
def OpenDocumentDrawing():
|
||||||
|
doc = OpenDocument('application/vnd.oasis.opendocument.graphics')
|
||||||
|
doc.drawing = Drawing()
|
||||||
|
doc.body.addElement(doc.drawing)
|
||||||
|
return doc
|
||||||
|
|
||||||
|
def OpenDocumentImage():
|
||||||
|
doc = OpenDocument('application/vnd.oasis.opendocument.image')
|
||||||
|
doc.image = Image()
|
||||||
|
doc.body.addElement(doc.image)
|
||||||
|
return doc
|
||||||
|
|
||||||
|
def OpenDocumentPresentation():
|
||||||
|
doc = OpenDocument('application/vnd.oasis.opendocument.presentation')
|
||||||
|
doc.presentation = Presentation()
|
||||||
|
doc.body.addElement(doc.presentation)
|
||||||
|
return doc
|
||||||
|
|
||||||
|
def OpenDocumentSpreadsheet():
|
||||||
|
doc = OpenDocument('application/vnd.oasis.opendocument.spreadsheet')
|
||||||
|
doc.spreadsheet = Spreadsheet()
|
||||||
|
doc.body.addElement(doc.spreadsheet)
|
||||||
|
return doc
|
||||||
|
|
||||||
|
def OpenDocumentText():
|
||||||
|
doc = OpenDocument('application/vnd.oasis.opendocument.text')
|
||||||
|
doc.text = Text()
|
||||||
|
doc.body.addElement(doc.text)
|
||||||
|
return doc
|
||||||
|
|
||||||
|
|
||||||
|
def load(odffile):
|
||||||
|
from load import LoadParser
|
||||||
|
from xml.sax import make_parser, handler
|
||||||
|
z = zipfile.ZipFile(odffile)
|
||||||
|
mimetype = z.read('mimetype')
|
||||||
|
doc = OpenDocument(mimetype, add_generator=False)
|
||||||
|
|
||||||
|
# Look in the manifest file to see if which of the four files there are
|
||||||
|
manifestpart = z.read('META-INF/manifest.xml')
|
||||||
|
manifest = manifestlist(manifestpart)
|
||||||
|
for xmlfile in ('settings.xml', 'meta.xml', 'content.xml', 'styles.xml'):
|
||||||
|
if not manifest.has_key(xmlfile):
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
xmlpart = z.read(xmlfile)
|
||||||
|
doc._parsing = xmlfile
|
||||||
|
|
||||||
|
parser = make_parser()
|
||||||
|
parser.setFeature(handler.feature_namespaces, 1)
|
||||||
|
parser.setContentHandler(LoadParser(doc))
|
||||||
|
parser.setErrorHandler(handler.ErrorHandler())
|
||||||
|
|
||||||
|
inpsrc = InputSource()
|
||||||
|
inpsrc.setByteStream(StringIO(xmlpart))
|
||||||
|
parser.parse(inpsrc)
|
||||||
|
del doc._parsing
|
||||||
|
except KeyError, v: pass
|
||||||
|
# Add the thumbnail here
|
||||||
|
# Add the images here
|
||||||
|
for mentry,mvalue in manifest.items():
|
||||||
|
if mentry[:9] == "Pictures/" and len(mentry) > 9:
|
||||||
|
doc.addPicture(mvalue['full-path'], mvalue['media-type'], z.read(mentry))
|
||||||
|
elif mentry == "Thumbnails/thumbnail.png":
|
||||||
|
doc.addThumbnail(z.read(mentry))
|
||||||
|
else:
|
||||||
|
pass # Add the SUN junk here to the struct somewhere
|
||||||
|
# It is cached data, so it can be out-of-date
|
||||||
|
z.close()
|
||||||
|
b = doc.getElementsByType(Body)
|
||||||
|
if mimetype[:39] == 'application/vnd.oasis.opendocument.text':
|
||||||
|
doc.text = b[0].firstChild
|
||||||
|
elif mimetype[:43] == 'application/vnd.oasis.opendocument.graphics':
|
||||||
|
doc.graphics = b[0].firstChild
|
||||||
|
elif mimetype[:47] == 'application/vnd.oasis.opendocument.presentation':
|
||||||
|
doc.presentation = b[0].firstChild
|
||||||
|
elif mimetype[:46] == 'application/vnd.oasis.opendocument.spreadsheet':
|
||||||
|
doc.spreadsheet = b[0].firstChild
|
||||||
|
elif mimetype[:40] == 'application/vnd.oasis.opendocument.chart':
|
||||||
|
doc.chart = b[0].firstChild
|
||||||
|
elif mimetype[:40] == 'application/vnd.oasis.opendocument.image':
|
||||||
|
doc.image = b[0].firstChild
|
||||||
|
elif mimetype[:42] == 'application/vnd.oasis.opendocument.formula':
|
||||||
|
doc.formula = b[0].firstChild
|
||||||
|
return doc
|
||||||
|
# vim: set expandtab sw=4 :
|
85
src/odf/presentation.py
Normal file
85
src/odf/presentation.py
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
|
||||||
|
#
|
||||||
|
# This library is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU Lesser General Public
|
||||||
|
# License as published by the Free Software Foundation; either
|
||||||
|
# version 2.1 of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This library is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
# Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public
|
||||||
|
# License along with this library; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
#
|
||||||
|
# Contributor(s):
|
||||||
|
#
|
||||||
|
|
||||||
|
from namespaces import PRESENTATIONNS
|
||||||
|
from element import Element
|
||||||
|
|
||||||
|
# ODF 1.0 section 9.6 and 9.7
|
||||||
|
# Autogenerated
|
||||||
|
def AnimationGroup(**args):
|
||||||
|
return Element(qname = (PRESENTATIONNS,'animation-group'), **args)
|
||||||
|
|
||||||
|
def Animations(**args):
|
||||||
|
return Element(qname = (PRESENTATIONNS,'animations'), **args)
|
||||||
|
|
||||||
|
def DateTime(**args):
|
||||||
|
return Element(qname = (PRESENTATIONNS,'date-time'), **args)
|
||||||
|
|
||||||
|
def DateTimeDecl(**args):
|
||||||
|
return Element(qname = (PRESENTATIONNS,'date-time-decl'), **args)
|
||||||
|
|
||||||
|
def Dim(**args):
|
||||||
|
return Element(qname = (PRESENTATIONNS,'dim'), **args)
|
||||||
|
|
||||||
|
def EventListener(**args):
|
||||||
|
return Element(qname = (PRESENTATIONNS,'event-listener'), **args)
|
||||||
|
|
||||||
|
def Footer(**args):
|
||||||
|
return Element(qname = (PRESENTATIONNS,'footer'), **args)
|
||||||
|
|
||||||
|
def FooterDecl(**args):
|
||||||
|
return Element(qname = (PRESENTATIONNS,'footer-decl'), **args)
|
||||||
|
|
||||||
|
def Header(**args):
|
||||||
|
return Element(qname = (PRESENTATIONNS,'header'), **args)
|
||||||
|
|
||||||
|
def HeaderDecl(**args):
|
||||||
|
return Element(qname = (PRESENTATIONNS,'header-decl'), **args)
|
||||||
|
|
||||||
|
def HideShape(**args):
|
||||||
|
return Element(qname = (PRESENTATIONNS,'hide-shape'), **args)
|
||||||
|
|
||||||
|
def HideText(**args):
|
||||||
|
return Element(qname = (PRESENTATIONNS,'hide-text'), **args)
|
||||||
|
|
||||||
|
def Notes(**args):
|
||||||
|
return Element(qname = (PRESENTATIONNS,'notes'), **args)
|
||||||
|
|
||||||
|
def Placeholder(**args):
|
||||||
|
return Element(qname = (PRESENTATIONNS,'placeholder'), **args)
|
||||||
|
|
||||||
|
def Play(**args):
|
||||||
|
return Element(qname = (PRESENTATIONNS,'play'), **args)
|
||||||
|
|
||||||
|
def Settings(**args):
|
||||||
|
return Element(qname = (PRESENTATIONNS,'settings'), **args)
|
||||||
|
|
||||||
|
def Show(**args):
|
||||||
|
return Element(qname = (PRESENTATIONNS,'show'), **args)
|
||||||
|
|
||||||
|
def ShowShape(**args):
|
||||||
|
return Element(qname = (PRESENTATIONNS,'show-shape'), **args)
|
||||||
|
|
||||||
|
def ShowText(**args):
|
||||||
|
return Element(qname = (PRESENTATIONNS,'show-text'), **args)
|
||||||
|
|
||||||
|
def Sound(**args):
|
||||||
|
return Element(qname = (PRESENTATIONNS,'sound'), **args)
|
||||||
|
|
30
src/odf/script.py
Normal file
30
src/odf/script.py
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
|
||||||
|
#
|
||||||
|
# This library is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU Lesser General Public
|
||||||
|
# License as published by the Free Software Foundation; either
|
||||||
|
# version 2.1 of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This library is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
# Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public
|
||||||
|
# License along with this library; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
#
|
||||||
|
# Contributor(s):
|
||||||
|
#
|
||||||
|
|
||||||
|
from namespaces import SCRIPTNS
|
||||||
|
from element import Element
|
||||||
|
|
||||||
|
# ODF 1.0 section 12.4.1
|
||||||
|
# The <script:event-listener> element binds an event to a macro.
|
||||||
|
|
||||||
|
# Autogenerated
|
||||||
|
def EventListener(**args):
|
||||||
|
return Element(qname = (SCRIPTNS,'event-listener'), **args)
|
||||||
|
|
148
src/odf/style.py
Normal file
148
src/odf/style.py
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
|
||||||
|
#
|
||||||
|
# This library is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU Lesser General Public
|
||||||
|
# License as published by the Free Software Foundation; either
|
||||||
|
# version 2.1 of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This library is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
# Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public
|
||||||
|
# License along with this library; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
#
|
||||||
|
# Contributor(s):
|
||||||
|
#
|
||||||
|
|
||||||
|
from namespaces import STYLENS
|
||||||
|
from element import Element
|
||||||
|
|
||||||
|
def StyleElement(**args):
|
||||||
|
e = Element(**args)
|
||||||
|
if args.get('check_grammar', True) == True:
|
||||||
|
if not args.has_key('displayname'):
|
||||||
|
e.setAttrNS(STYLENS,'display-name', args.get('name'))
|
||||||
|
return e
|
||||||
|
|
||||||
|
# Autogenerated
|
||||||
|
def BackgroundImage(**args):
|
||||||
|
return Element(qname = (STYLENS,'background-image'), **args)
|
||||||
|
|
||||||
|
def ChartProperties(**args):
|
||||||
|
return Element(qname = (STYLENS,'chart-properties'), **args)
|
||||||
|
|
||||||
|
def Column(**args):
|
||||||
|
return Element(qname = (STYLENS,'column'), **args)
|
||||||
|
|
||||||
|
def ColumnSep(**args):
|
||||||
|
return Element(qname = (STYLENS,'column-sep'), **args)
|
||||||
|
|
||||||
|
def Columns(**args):
|
||||||
|
return Element(qname = (STYLENS,'columns'), **args)
|
||||||
|
|
||||||
|
def DefaultStyle(**args):
|
||||||
|
return Element(qname = (STYLENS,'default-style'), **args)
|
||||||
|
|
||||||
|
def DrawingPageProperties(**args):
|
||||||
|
return Element(qname = (STYLENS,'drawing-page-properties'), **args)
|
||||||
|
|
||||||
|
def DropCap(**args):
|
||||||
|
return Element(qname = (STYLENS,'drop-cap'), **args)
|
||||||
|
|
||||||
|
def FontFace(**args):
|
||||||
|
return Element(qname = (STYLENS,'font-face'), **args)
|
||||||
|
|
||||||
|
def Footer(**args):
|
||||||
|
return Element(qname = (STYLENS,'footer'), **args)
|
||||||
|
|
||||||
|
def FooterLeft(**args):
|
||||||
|
return Element(qname = (STYLENS,'footer-left'), **args)
|
||||||
|
|
||||||
|
def FooterStyle(**args):
|
||||||
|
return Element(qname = (STYLENS,'footer-style'), **args)
|
||||||
|
|
||||||
|
def FootnoteSep(**args):
|
||||||
|
return Element(qname = (STYLENS,'footnote-sep'), **args)
|
||||||
|
|
||||||
|
def GraphicProperties(**args):
|
||||||
|
return Element(qname = (STYLENS,'graphic-properties'), **args)
|
||||||
|
|
||||||
|
def HandoutMaster(**args):
|
||||||
|
return Element(qname = (STYLENS,'handout-master'), **args)
|
||||||
|
|
||||||
|
def Header(**args):
|
||||||
|
return Element(qname = (STYLENS,'header'), **args)
|
||||||
|
|
||||||
|
def HeaderFooterProperties(**args):
|
||||||
|
return Element(qname = (STYLENS,'header-footer-properties'), **args)
|
||||||
|
|
||||||
|
def HeaderLeft(**args):
|
||||||
|
return Element(qname = (STYLENS,'header-left'), **args)
|
||||||
|
|
||||||
|
def HeaderStyle(**args):
|
||||||
|
return Element(qname = (STYLENS,'header-style'), **args)
|
||||||
|
|
||||||
|
def ListLevelProperties(**args):
|
||||||
|
return Element(qname = (STYLENS,'list-level-properties'), **args)
|
||||||
|
|
||||||
|
def Map(**args):
|
||||||
|
return Element(qname = (STYLENS,'map'), **args)
|
||||||
|
|
||||||
|
def MasterPage(**args):
|
||||||
|
return StyleElement(qname = (STYLENS,'master-page'), **args)
|
||||||
|
|
||||||
|
def PageLayout(**args):
|
||||||
|
return Element(qname = (STYLENS,'page-layout'), **args)
|
||||||
|
|
||||||
|
def PageLayoutProperties(**args):
|
||||||
|
return Element(qname = (STYLENS,'page-layout-properties'), **args)
|
||||||
|
|
||||||
|
def ParagraphProperties(**args):
|
||||||
|
return Element(qname = (STYLENS,'paragraph-properties'), **args)
|
||||||
|
|
||||||
|
def PresentationPageLayout(**args):
|
||||||
|
return StyleElement(qname = (STYLENS,'presentation-page-layout'), **args)
|
||||||
|
|
||||||
|
def RegionCenter(**args):
|
||||||
|
return Element(qname = (STYLENS,'region-center'), **args)
|
||||||
|
|
||||||
|
def RegionLeft(**args):
|
||||||
|
return Element(qname = (STYLENS,'region-left'), **args)
|
||||||
|
|
||||||
|
def RegionRight(**args):
|
||||||
|
return Element(qname = (STYLENS,'region-right'), **args)
|
||||||
|
|
||||||
|
def RubyProperties(**args):
|
||||||
|
return Element(qname = (STYLENS,'ruby-properties'), **args)
|
||||||
|
|
||||||
|
def SectionProperties(**args):
|
||||||
|
return Element(qname = (STYLENS,'section-properties'), **args)
|
||||||
|
|
||||||
|
def Style(**args):
|
||||||
|
return StyleElement(qname = (STYLENS,'style'), **args)
|
||||||
|
|
||||||
|
def TabStop(**args):
|
||||||
|
return Element(qname = (STYLENS,'tab-stop'), **args)
|
||||||
|
|
||||||
|
def TabStops(**args):
|
||||||
|
return Element(qname = (STYLENS,'tab-stops'), **args)
|
||||||
|
|
||||||
|
def TableCellProperties(**args):
|
||||||
|
return Element(qname = (STYLENS,'table-cell-properties'), **args)
|
||||||
|
|
||||||
|
def TableColumnProperties(**args):
|
||||||
|
return Element(qname = (STYLENS,'table-column-properties'), **args)
|
||||||
|
|
||||||
|
def TableProperties(**args):
|
||||||
|
return Element(qname = (STYLENS,'table-properties'), **args)
|
||||||
|
|
||||||
|
def TableRowProperties(**args):
|
||||||
|
return Element(qname = (STYLENS,'table-row-properties'), **args)
|
||||||
|
|
||||||
|
def TextProperties(**args):
|
||||||
|
return Element(qname = (STYLENS,'text-properties'), **args)
|
||||||
|
|
52
src/odf/svg.py
Normal file
52
src/odf/svg.py
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
|
||||||
|
#
|
||||||
|
# This library is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU Lesser General Public
|
||||||
|
# License as published by the Free Software Foundation; either
|
||||||
|
# version 2.1 of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This library is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
# Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public
|
||||||
|
# License along with this library; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
#
|
||||||
|
# Contributor(s):
|
||||||
|
#
|
||||||
|
|
||||||
|
from namespaces import SVGNS
|
||||||
|
from element import Element
|
||||||
|
from draw import DrawElement
|
||||||
|
|
||||||
|
# Autogenerated
|
||||||
|
def DefinitionSrc(**args):
|
||||||
|
return Element(qname = (SVGNS,'definition-src'), **args)
|
||||||
|
|
||||||
|
def Desc(**args):
|
||||||
|
return Element(qname = (SVGNS,'desc'), **args)
|
||||||
|
|
||||||
|
def FontFaceFormat(**args):
|
||||||
|
return Element(qname = (SVGNS,'font-face-format'), **args)
|
||||||
|
|
||||||
|
def FontFaceName(**args):
|
||||||
|
return Element(qname = (SVGNS,'font-face-name'), **args)
|
||||||
|
|
||||||
|
def FontFaceSrc(**args):
|
||||||
|
return Element(qname = (SVGNS,'font-face-src'), **args)
|
||||||
|
|
||||||
|
def FontFaceUri(**args):
|
||||||
|
return Element(qname = (SVGNS,'font-face-uri'), **args)
|
||||||
|
|
||||||
|
def Lineargradient(**args):
|
||||||
|
return DrawElement(qname = (SVGNS,'linearGradient'), **args)
|
||||||
|
|
||||||
|
def Radialgradient(**args):
|
||||||
|
return DrawElement(qname = (SVGNS,'radialGradient'), **args)
|
||||||
|
|
||||||
|
def Stop(**args):
|
||||||
|
return Element(qname = (SVGNS,'stop'), **args)
|
||||||
|
|
307
src/odf/table.py
Normal file
307
src/odf/table.py
Normal file
@ -0,0 +1,307 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
|
||||||
|
#
|
||||||
|
# This library is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU Lesser General Public
|
||||||
|
# License as published by the Free Software Foundation; either
|
||||||
|
# version 2.1 of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This library is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
# Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public
|
||||||
|
# License along with this library; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
#
|
||||||
|
# Contributor(s):
|
||||||
|
#
|
||||||
|
|
||||||
|
from namespaces import TABLENS
|
||||||
|
from element import Element
|
||||||
|
|
||||||
|
|
||||||
|
# Autogenerated
|
||||||
|
def Body(**args):
|
||||||
|
return Element(qname = (TABLENS,'body'), **args)
|
||||||
|
|
||||||
|
def CalculationSettings(**args):
|
||||||
|
return Element(qname = (TABLENS,'calculation-settings'), **args)
|
||||||
|
|
||||||
|
def CellAddress(**args):
|
||||||
|
return Element(qname = (TABLENS,'cell-address'), **args)
|
||||||
|
|
||||||
|
def CellContentChange(**args):
|
||||||
|
return Element(qname = (TABLENS,'cell-content-change'), **args)
|
||||||
|
|
||||||
|
def CellContentDeletion(**args):
|
||||||
|
return Element(qname = (TABLENS,'cell-content-deletion'), **args)
|
||||||
|
|
||||||
|
def CellRangeSource(**args):
|
||||||
|
return Element(qname = (TABLENS,'cell-range-source'), **args)
|
||||||
|
|
||||||
|
def ChangeDeletion(**args):
|
||||||
|
return Element(qname = (TABLENS,'change-deletion'), **args)
|
||||||
|
|
||||||
|
def ChangeTrackTableCell(**args):
|
||||||
|
return Element(qname = (TABLENS,'change-track-table-cell'), **args)
|
||||||
|
|
||||||
|
def Consolidation(**args):
|
||||||
|
return Element(qname = (TABLENS,'consolidation'), **args)
|
||||||
|
|
||||||
|
def ContentValidation(**args):
|
||||||
|
return Element(qname = (TABLENS,'content-validation'), **args)
|
||||||
|
|
||||||
|
def ContentValidations(**args):
|
||||||
|
return Element(qname = (TABLENS,'content-validations'), **args)
|
||||||
|
|
||||||
|
def CoveredTableCell(**args):
|
||||||
|
return Element(qname = (TABLENS,'covered-table-cell'), **args)
|
||||||
|
|
||||||
|
def CutOffs(**args):
|
||||||
|
return Element(qname = (TABLENS,'cut-offs'), **args)
|
||||||
|
|
||||||
|
def DataPilotDisplayInfo(**args):
|
||||||
|
return Element(qname = (TABLENS,'data-pilot-display-info'), **args)
|
||||||
|
|
||||||
|
def DataPilotField(**args):
|
||||||
|
return Element(qname = (TABLENS,'data-pilot-field'), **args)
|
||||||
|
|
||||||
|
def DataPilotFieldReference(**args):
|
||||||
|
return Element(qname = (TABLENS,'data-pilot-field-reference'), **args)
|
||||||
|
|
||||||
|
def DataPilotGroup(**args):
|
||||||
|
return Element(qname = (TABLENS,'data-pilot-group'), **args)
|
||||||
|
|
||||||
|
def DataPilotGroupMember(**args):
|
||||||
|
return Element(qname = (TABLENS,'data-pilot-group-member'), **args)
|
||||||
|
|
||||||
|
def DataPilotGroups(**args):
|
||||||
|
return Element(qname = (TABLENS,'data-pilot-groups'), **args)
|
||||||
|
|
||||||
|
def DataPilotLayoutInfo(**args):
|
||||||
|
return Element(qname = (TABLENS,'data-pilot-layout-info'), **args)
|
||||||
|
|
||||||
|
def DataPilotLevel(**args):
|
||||||
|
return Element(qname = (TABLENS,'data-pilot-level'), **args)
|
||||||
|
|
||||||
|
def DataPilotMember(**args):
|
||||||
|
return Element(qname = (TABLENS,'data-pilot-member'), **args)
|
||||||
|
|
||||||
|
def DataPilotMembers(**args):
|
||||||
|
return Element(qname = (TABLENS,'data-pilot-members'), **args)
|
||||||
|
|
||||||
|
def DataPilotSortInfo(**args):
|
||||||
|
return Element(qname = (TABLENS,'data-pilot-sort-info'), **args)
|
||||||
|
|
||||||
|
def DataPilotSubtotal(**args):
|
||||||
|
return Element(qname = (TABLENS,'data-pilot-subtotal'), **args)
|
||||||
|
|
||||||
|
def DataPilotSubtotals(**args):
|
||||||
|
return Element(qname = (TABLENS,'data-pilot-subtotals'), **args)
|
||||||
|
|
||||||
|
def DataPilotTable(**args):
|
||||||
|
return Element(qname = (TABLENS,'data-pilot-table'), **args)
|
||||||
|
|
||||||
|
def DataPilotTables(**args):
|
||||||
|
return Element(qname = (TABLENS,'data-pilot-tables'), **args)
|
||||||
|
|
||||||
|
def DatabaseRange(**args):
|
||||||
|
return Element(qname = (TABLENS,'database-range'), **args)
|
||||||
|
|
||||||
|
def DatabaseRanges(**args):
|
||||||
|
return Element(qname = (TABLENS,'database-ranges'), **args)
|
||||||
|
|
||||||
|
def DatabaseSourceQuery(**args):
|
||||||
|
return Element(qname = (TABLENS,'database-source-query'), **args)
|
||||||
|
|
||||||
|
def DatabaseSourceSql(**args):
|
||||||
|
return Element(qname = (TABLENS,'database-source-sql'), **args)
|
||||||
|
|
||||||
|
def DatabaseSourceTable(**args):
|
||||||
|
return Element(qname = (TABLENS,'database-source-table'), **args)
|
||||||
|
|
||||||
|
def DdeLink(**args):
|
||||||
|
return Element(qname = (TABLENS,'dde-link'), **args)
|
||||||
|
|
||||||
|
def DdeLinks(**args):
|
||||||
|
return Element(qname = (TABLENS,'dde-links'), **args)
|
||||||
|
|
||||||
|
def Deletion(**args):
|
||||||
|
return Element(qname = (TABLENS,'deletion'), **args)
|
||||||
|
|
||||||
|
def Deletions(**args):
|
||||||
|
return Element(qname = (TABLENS,'deletions'), **args)
|
||||||
|
|
||||||
|
def Dependencies(**args):
|
||||||
|
return Element(qname = (TABLENS,'dependencies'), **args)
|
||||||
|
|
||||||
|
def Dependency(**args):
|
||||||
|
return Element(qname = (TABLENS,'dependency'), **args)
|
||||||
|
|
||||||
|
def Detective(**args):
|
||||||
|
return Element(qname = (TABLENS,'detective'), **args)
|
||||||
|
|
||||||
|
def ErrorMacro(**args):
|
||||||
|
return Element(qname = (TABLENS,'error-macro'), **args)
|
||||||
|
|
||||||
|
def ErrorMessage(**args):
|
||||||
|
return Element(qname = (TABLENS,'error-message'), **args)
|
||||||
|
|
||||||
|
def EvenColumns(**args):
|
||||||
|
return Element(qname = (TABLENS,'even-columns'), **args)
|
||||||
|
|
||||||
|
def EvenRows(**args):
|
||||||
|
return Element(qname = (TABLENS,'even-rows'), **args)
|
||||||
|
|
||||||
|
def Filter(**args):
|
||||||
|
return Element(qname = (TABLENS,'filter'), **args)
|
||||||
|
|
||||||
|
def FilterAnd(**args):
|
||||||
|
return Element(qname = (TABLENS,'filter-and'), **args)
|
||||||
|
|
||||||
|
def FilterCondition(**args):
|
||||||
|
return Element(qname = (TABLENS,'filter-condition'), **args)
|
||||||
|
|
||||||
|
def FilterOr(**args):
|
||||||
|
return Element(qname = (TABLENS,'filter-or'), **args)
|
||||||
|
|
||||||
|
def FirstColumn(**args):
|
||||||
|
return Element(qname = (TABLENS,'first-column'), **args)
|
||||||
|
|
||||||
|
def FirstRow(**args):
|
||||||
|
return Element(qname = (TABLENS,'first-row'), **args)
|
||||||
|
|
||||||
|
def HelpMessage(**args):
|
||||||
|
return Element(qname = (TABLENS,'help-message'), **args)
|
||||||
|
|
||||||
|
def HighlightedRange(**args):
|
||||||
|
return Element(qname = (TABLENS,'highlighted-range'), **args)
|
||||||
|
|
||||||
|
def Insertion(**args):
|
||||||
|
return Element(qname = (TABLENS,'insertion'), **args)
|
||||||
|
|
||||||
|
def InsertionCutOff(**args):
|
||||||
|
return Element(qname = (TABLENS,'insertion-cut-off'), **args)
|
||||||
|
|
||||||
|
def Iteration(**args):
|
||||||
|
return Element(qname = (TABLENS,'iteration'), **args)
|
||||||
|
|
||||||
|
def LabelRange(**args):
|
||||||
|
return Element(qname = (TABLENS,'label-range'), **args)
|
||||||
|
|
||||||
|
def LabelRanges(**args):
|
||||||
|
return Element(qname = (TABLENS,'label-ranges'), **args)
|
||||||
|
|
||||||
|
def LastColumn(**args):
|
||||||
|
return Element(qname = (TABLENS,'last-column'), **args)
|
||||||
|
|
||||||
|
def LastRow(**args):
|
||||||
|
return Element(qname = (TABLENS,'last-row'), **args)
|
||||||
|
|
||||||
|
def Movement(**args):
|
||||||
|
return Element(qname = (TABLENS,'movement'), **args)
|
||||||
|
|
||||||
|
def MovementCutOff(**args):
|
||||||
|
return Element(qname = (TABLENS,'movement-cut-off'), **args)
|
||||||
|
|
||||||
|
def NamedExpression(**args):
|
||||||
|
return Element(qname = (TABLENS,'named-expression'), **args)
|
||||||
|
|
||||||
|
def NamedExpressions(**args):
|
||||||
|
return Element(qname = (TABLENS,'named-expressions'), **args)
|
||||||
|
|
||||||
|
def NamedRange(**args):
|
||||||
|
return Element(qname = (TABLENS,'named-range'), **args)
|
||||||
|
|
||||||
|
def NullDate(**args):
|
||||||
|
return Element(qname = (TABLENS,'null-date'), **args)
|
||||||
|
|
||||||
|
def OddColumns(**args):
|
||||||
|
return Element(qname = (TABLENS,'odd-columns'), **args)
|
||||||
|
|
||||||
|
def OddRows(**args):
|
||||||
|
return Element(qname = (TABLENS,'odd-rows'), **args)
|
||||||
|
|
||||||
|
def Operation(**args):
|
||||||
|
return Element(qname = (TABLENS,'operation'), **args)
|
||||||
|
|
||||||
|
def Previous(**args):
|
||||||
|
return Element(qname = (TABLENS,'previous'), **args)
|
||||||
|
|
||||||
|
def Scenario(**args):
|
||||||
|
return Element(qname = (TABLENS,'scenario'), **args)
|
||||||
|
|
||||||
|
def Shapes(**args):
|
||||||
|
return Element(qname = (TABLENS,'shapes'), **args)
|
||||||
|
|
||||||
|
def Sort(**args):
|
||||||
|
return Element(qname = (TABLENS,'sort'), **args)
|
||||||
|
|
||||||
|
def SortBy(**args):
|
||||||
|
return Element(qname = (TABLENS,'sort-by'), **args)
|
||||||
|
|
||||||
|
def SortGroups(**args):
|
||||||
|
return Element(qname = (TABLENS,'sort-groups'), **args)
|
||||||
|
|
||||||
|
def SourceCellRange(**args):
|
||||||
|
return Element(qname = (TABLENS,'source-cell-range'), **args)
|
||||||
|
|
||||||
|
def SourceRangeAddress(**args):
|
||||||
|
return Element(qname = (TABLENS,'source-range-address'), **args)
|
||||||
|
|
||||||
|
def SourceService(**args):
|
||||||
|
return Element(qname = (TABLENS,'source-service'), **args)
|
||||||
|
|
||||||
|
def SubtotalField(**args):
|
||||||
|
return Element(qname = (TABLENS,'subtotal-field'), **args)
|
||||||
|
|
||||||
|
def SubtotalRule(**args):
|
||||||
|
return Element(qname = (TABLENS,'subtotal-rule'), **args)
|
||||||
|
|
||||||
|
def SubtotalRules(**args):
|
||||||
|
return Element(qname = (TABLENS,'subtotal-rules'), **args)
|
||||||
|
|
||||||
|
def Table(**args):
|
||||||
|
return Element(qname = (TABLENS,'table'), **args)
|
||||||
|
|
||||||
|
def TableCell(**args):
|
||||||
|
return Element(qname = (TABLENS,'table-cell'), **args)
|
||||||
|
|
||||||
|
def TableColumn(**args):
|
||||||
|
return Element(qname = (TABLENS,'table-column'), **args)
|
||||||
|
|
||||||
|
def TableColumnGroup(**args):
|
||||||
|
return Element(qname = (TABLENS,'table-column-group'), **args)
|
||||||
|
|
||||||
|
def TableColumns(**args):
|
||||||
|
return Element(qname = (TABLENS,'table-columns'), **args)
|
||||||
|
|
||||||
|
def TableHeaderColumns(**args):
|
||||||
|
return Element(qname = (TABLENS,'table-header-columns'), **args)
|
||||||
|
|
||||||
|
def TableHeaderRows(**args):
|
||||||
|
return Element(qname = (TABLENS,'table-header-rows'), **args)
|
||||||
|
|
||||||
|
def TableRow(**args):
|
||||||
|
return Element(qname = (TABLENS,'table-row'), **args)
|
||||||
|
|
||||||
|
def TableRowGroup(**args):
|
||||||
|
return Element(qname = (TABLENS,'table-row-group'), **args)
|
||||||
|
|
||||||
|
def TableRows(**args):
|
||||||
|
return Element(qname = (TABLENS,'table-rows'), **args)
|
||||||
|
|
||||||
|
def TableSource(**args):
|
||||||
|
return Element(qname = (TABLENS,'table-source'), **args)
|
||||||
|
|
||||||
|
def TableTemplate(**args):
|
||||||
|
return Element(qname = (TABLENS,'table-template'), **args)
|
||||||
|
|
||||||
|
def TargetRangeAddress(**args):
|
||||||
|
return Element(qname = (TABLENS,'target-range-address'), **args)
|
||||||
|
|
||||||
|
def TrackedChanges(**args):
|
||||||
|
return Element(qname = (TABLENS,'tracked-changes'), **args)
|
||||||
|
|
137
src/odf/teletype.py
Normal file
137
src/odf/teletype.py
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# Create and extract text from ODF, handling whitespace correctly.
|
||||||
|
# Copyright (C) 2008 J. David Eisenberg
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License along
|
||||||
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
Class for handling whitespace properly in OpenDocument.
|
||||||
|
|
||||||
|
While it is possible to use getTextContent() and setTextContent()
|
||||||
|
to extract or create ODF content, these won't extract or create
|
||||||
|
the appropriate <text:s>, <text:tab>, or <text:line-break>
|
||||||
|
elements. This module takes care of that problem.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from odf.element import Node
|
||||||
|
import odf.opendocument
|
||||||
|
from odf.text import S,LineBreak,Tab
|
||||||
|
|
||||||
|
class WhitespaceText(object):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.textBuffer = []
|
||||||
|
self.spaceCount = 0
|
||||||
|
|
||||||
|
def addTextToElement(self, odfElement, s):
|
||||||
|
""" Process an input string, inserting
|
||||||
|
<text:tab> elements for '\t',
|
||||||
|
<text:line-break> elements for '\n', and
|
||||||
|
<text:s> elements for runs of more than one blank.
|
||||||
|
These will be added to the given element.
|
||||||
|
"""
|
||||||
|
i = 0
|
||||||
|
ch = ' '
|
||||||
|
|
||||||
|
# When we encounter a tab or newline, we can immediately
|
||||||
|
# dump any accumulated text and then emit the appropriate
|
||||||
|
# ODF element.
|
||||||
|
#
|
||||||
|
# When we encounter a space, we add it to the text buffer,
|
||||||
|
# and then collect more spaces. If there are more spaces
|
||||||
|
# after the first one, we dump the text buffer and then
|
||||||
|
# then emit the appropriate <text:s> element.
|
||||||
|
|
||||||
|
while i < len(s):
|
||||||
|
ch = s[i]
|
||||||
|
if ch == '\t':
|
||||||
|
self._emitTextBuffer(odfElement)
|
||||||
|
odfElement.addElement(Tab())
|
||||||
|
i += 1
|
||||||
|
elif ch == '\n':
|
||||||
|
self._emitTextBuffer(odfElement);
|
||||||
|
odfElement.addElement(LineBreak())
|
||||||
|
i += 1
|
||||||
|
elif ch == ' ':
|
||||||
|
self.textBuffer.append(' ')
|
||||||
|
i += 1
|
||||||
|
self.spaceCount = 0
|
||||||
|
while i < len(s) and (s[i] == ' '):
|
||||||
|
self.spaceCount += 1
|
||||||
|
i += 1
|
||||||
|
if self.spaceCount > 0:
|
||||||
|
self._emitTextBuffer(odfElement)
|
||||||
|
self._emitSpaces(odfElement)
|
||||||
|
else:
|
||||||
|
self.textBuffer.append(ch)
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
self._emitTextBuffer(odfElement)
|
||||||
|
|
||||||
|
def _emitTextBuffer(self, odfElement):
|
||||||
|
""" Creates a Text Node whose contents are the current textBuffer.
|
||||||
|
Side effect: clears the text buffer.
|
||||||
|
"""
|
||||||
|
if len(self.textBuffer) > 0:
|
||||||
|
odfElement.addText(''.join(self.textBuffer))
|
||||||
|
self.textBuffer = []
|
||||||
|
|
||||||
|
|
||||||
|
def _emitSpaces(self, odfElement):
|
||||||
|
""" Creates a <text:s> element for the current spaceCount.
|
||||||
|
Side effect: sets spaceCount back to zero
|
||||||
|
"""
|
||||||
|
if self.spaceCount > 0:
|
||||||
|
spaceElement = S(c=self.spaceCount)
|
||||||
|
odfElement.addElement(spaceElement)
|
||||||
|
self.spaceCount = 0
|
||||||
|
|
||||||
|
def addTextToElement(odfElement, s):
|
||||||
|
wst = WhitespaceText()
|
||||||
|
wst.addTextToElement(odfElement, s)
|
||||||
|
|
||||||
|
def extractText(odfElement):
|
||||||
|
""" Extract text content from an Element, with whitespace represented
|
||||||
|
properly. Returns the text, with tabs, spaces, and newlines
|
||||||
|
correctly evaluated. This method recursively descends through the
|
||||||
|
children of the given element, accumulating text and "unwrapping"
|
||||||
|
<text:s>, <text:tab>, and <text:line-break> elements along the way.
|
||||||
|
"""
|
||||||
|
result = [];
|
||||||
|
|
||||||
|
if len(odfElement.childNodes) != 0:
|
||||||
|
for child in odfElement.childNodes:
|
||||||
|
if child.nodeType == Node.TEXT_NODE:
|
||||||
|
result.append(child.data)
|
||||||
|
elif child.nodeType == Node.ELEMENT_NODE:
|
||||||
|
subElement = child
|
||||||
|
tagName = subElement.qname;
|
||||||
|
if tagName == (u"urn:oasis:names:tc:opendocument:xmlns:text:1.0", u"line-break"):
|
||||||
|
result.append("\n")
|
||||||
|
elif tagName == (u"urn:oasis:names:tc:opendocument:xmlns:text:1.0", u"tab"):
|
||||||
|
result.append("\t")
|
||||||
|
elif tagName == (u"urn:oasis:names:tc:opendocument:xmlns:text:1.0", u"s"):
|
||||||
|
c = subElement.getAttribute('c')
|
||||||
|
if c:
|
||||||
|
spaceCount = int(c)
|
||||||
|
else:
|
||||||
|
spaceCount = 1
|
||||||
|
|
||||||
|
result.append(" " * spaceCount)
|
||||||
|
else:
|
||||||
|
result.append(extractText(subElement))
|
||||||
|
return ''.join(result)
|
559
src/odf/text.py
Normal file
559
src/odf/text.py
Normal file
@ -0,0 +1,559 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
|
||||||
|
#
|
||||||
|
# This library is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU Lesser General Public
|
||||||
|
# License as published by the Free Software Foundation; either
|
||||||
|
# version 2.1 of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This library is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
# Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public
|
||||||
|
# License along with this library; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
#
|
||||||
|
# Contributor(s):
|
||||||
|
#
|
||||||
|
|
||||||
|
from namespaces import TEXTNS
|
||||||
|
from element import Element
|
||||||
|
from style import StyleElement
|
||||||
|
|
||||||
|
# Autogenerated
|
||||||
|
def A(**args):
|
||||||
|
return Element(qname = (TEXTNS,'a'), **args)
|
||||||
|
|
||||||
|
def AlphabeticalIndex(**args):
|
||||||
|
return Element(qname = (TEXTNS,'alphabetical-index'), **args)
|
||||||
|
|
||||||
|
def AlphabeticalIndexAutoMarkFile(**args):
|
||||||
|
return Element(qname = (TEXTNS,'alphabetical-index-auto-mark-file'), **args)
|
||||||
|
|
||||||
|
def AlphabeticalIndexEntryTemplate(**args):
|
||||||
|
return Element(qname = (TEXTNS,'alphabetical-index-entry-template'), **args)
|
||||||
|
|
||||||
|
def AlphabeticalIndexMark(**args):
|
||||||
|
return Element(qname = (TEXTNS,'alphabetical-index-mark'), **args)
|
||||||
|
|
||||||
|
def AlphabeticalIndexMarkEnd(**args):
|
||||||
|
return Element(qname = (TEXTNS,'alphabetical-index-mark-end'), **args)
|
||||||
|
|
||||||
|
def AlphabeticalIndexMarkStart(**args):
|
||||||
|
return Element(qname = (TEXTNS,'alphabetical-index-mark-start'), **args)
|
||||||
|
|
||||||
|
def AlphabeticalIndexSource(**args):
|
||||||
|
return Element(qname = (TEXTNS,'alphabetical-index-source'), **args)
|
||||||
|
|
||||||
|
def AuthorInitials(**args):
|
||||||
|
return Element(qname = (TEXTNS,'author-initials'), **args)
|
||||||
|
|
||||||
|
def AuthorName(**args):
|
||||||
|
return Element(qname = (TEXTNS,'author-name'), **args)
|
||||||
|
|
||||||
|
def Bibliography(**args):
|
||||||
|
return Element(qname = (TEXTNS,'bibliography'), **args)
|
||||||
|
|
||||||
|
def BibliographyConfiguration(**args):
|
||||||
|
return Element(qname = (TEXTNS,'bibliography-configuration'), **args)
|
||||||
|
|
||||||
|
def BibliographyEntryTemplate(**args):
|
||||||
|
return Element(qname = (TEXTNS,'bibliography-entry-template'), **args)
|
||||||
|
|
||||||
|
def BibliographyMark(**args):
|
||||||
|
return Element(qname = (TEXTNS,'bibliography-mark'), **args)
|
||||||
|
|
||||||
|
def BibliographySource(**args):
|
||||||
|
return Element(qname = (TEXTNS,'bibliography-source'), **args)
|
||||||
|
|
||||||
|
def Bookmark(**args):
|
||||||
|
return Element(qname = (TEXTNS,'bookmark'), **args)
|
||||||
|
|
||||||
|
def BookmarkEnd(**args):
|
||||||
|
return Element(qname = (TEXTNS,'bookmark-end'), **args)
|
||||||
|
|
||||||
|
def BookmarkRef(**args):
|
||||||
|
return Element(qname = (TEXTNS,'bookmark-ref'), **args)
|
||||||
|
|
||||||
|
def BookmarkStart(**args):
|
||||||
|
return Element(qname = (TEXTNS,'bookmark-start'), **args)
|
||||||
|
|
||||||
|
def Change(**args):
|
||||||
|
return Element(qname = (TEXTNS,'change'), **args)
|
||||||
|
|
||||||
|
def ChangeEnd(**args):
|
||||||
|
return Element(qname = (TEXTNS,'change-end'), **args)
|
||||||
|
|
||||||
|
def ChangeStart(**args):
|
||||||
|
return Element(qname = (TEXTNS,'change-start'), **args)
|
||||||
|
|
||||||
|
def ChangedRegion(**args):
|
||||||
|
return Element(qname = (TEXTNS,'changed-region'), **args)
|
||||||
|
|
||||||
|
def Chapter(**args):
|
||||||
|
return Element(qname = (TEXTNS,'chapter'), **args)
|
||||||
|
|
||||||
|
def CharacterCount(**args):
|
||||||
|
return Element(qname = (TEXTNS,'character-count'), **args)
|
||||||
|
|
||||||
|
def ConditionalText(**args):
|
||||||
|
return Element(qname = (TEXTNS,'conditional-text'), **args)
|
||||||
|
|
||||||
|
def CreationDate(**args):
|
||||||
|
return Element(qname = (TEXTNS,'creation-date'), **args)
|
||||||
|
|
||||||
|
def CreationTime(**args):
|
||||||
|
return Element(qname = (TEXTNS,'creation-time'), **args)
|
||||||
|
|
||||||
|
def Creator(**args):
|
||||||
|
return Element(qname = (TEXTNS,'creator'), **args)
|
||||||
|
|
||||||
|
def DatabaseDisplay(**args):
|
||||||
|
return Element(qname = (TEXTNS,'database-display'), **args)
|
||||||
|
|
||||||
|
def DatabaseName(**args):
|
||||||
|
return Element(qname = (TEXTNS,'database-name'), **args)
|
||||||
|
|
||||||
|
def DatabaseNext(**args):
|
||||||
|
return Element(qname = (TEXTNS,'database-next'), **args)
|
||||||
|
|
||||||
|
def DatabaseRowNumber(**args):
|
||||||
|
return Element(qname = (TEXTNS,'database-row-number'), **args)
|
||||||
|
|
||||||
|
def DatabaseRowSelect(**args):
|
||||||
|
return Element(qname = (TEXTNS,'database-row-select'), **args)
|
||||||
|
|
||||||
|
def Date(**args):
|
||||||
|
return Element(qname = (TEXTNS,'date'), **args)
|
||||||
|
|
||||||
|
def DdeConnection(**args):
|
||||||
|
return Element(qname = (TEXTNS,'dde-connection'), **args)
|
||||||
|
|
||||||
|
def DdeConnectionDecl(**args):
|
||||||
|
return Element(qname = (TEXTNS,'dde-connection-decl'), **args)
|
||||||
|
|
||||||
|
def DdeConnectionDecls(**args):
|
||||||
|
return Element(qname = (TEXTNS,'dde-connection-decls'), **args)
|
||||||
|
|
||||||
|
def Deletion(**args):
|
||||||
|
return Element(qname = (TEXTNS,'deletion'), **args)
|
||||||
|
|
||||||
|
def Description(**args):
|
||||||
|
return Element(qname = (TEXTNS,'description'), **args)
|
||||||
|
|
||||||
|
def EditingCycles(**args):
|
||||||
|
return Element(qname = (TEXTNS,'editing-cycles'), **args)
|
||||||
|
|
||||||
|
def EditingDuration(**args):
|
||||||
|
return Element(qname = (TEXTNS,'editing-duration'), **args)
|
||||||
|
|
||||||
|
def ExecuteMacro(**args):
|
||||||
|
return Element(qname = (TEXTNS,'execute-macro'), **args)
|
||||||
|
|
||||||
|
def Expression(**args):
|
||||||
|
return Element(qname = (TEXTNS,'expression'), **args)
|
||||||
|
|
||||||
|
def FileName(**args):
|
||||||
|
return Element(qname = (TEXTNS,'file-name'), **args)
|
||||||
|
|
||||||
|
def FormatChange(**args):
|
||||||
|
return Element(qname = (TEXTNS,'format-change'), **args)
|
||||||
|
|
||||||
|
def H(**args):
|
||||||
|
return Element(qname = (TEXTNS, 'h'), **args)
|
||||||
|
|
||||||
|
def HiddenParagraph(**args):
|
||||||
|
return Element(qname = (TEXTNS,'hidden-paragraph'), **args)
|
||||||
|
|
||||||
|
def HiddenText(**args):
|
||||||
|
return Element(qname = (TEXTNS,'hidden-text'), **args)
|
||||||
|
|
||||||
|
def IllustrationIndex(**args):
|
||||||
|
return Element(qname = (TEXTNS,'illustration-index'), **args)
|
||||||
|
|
||||||
|
def IllustrationIndexEntryTemplate(**args):
|
||||||
|
return Element(qname = (TEXTNS,'illustration-index-entry-template'), **args)
|
||||||
|
|
||||||
|
def IllustrationIndexSource(**args):
|
||||||
|
return Element(qname = (TEXTNS,'illustration-index-source'), **args)
|
||||||
|
|
||||||
|
def ImageCount(**args):
|
||||||
|
return Element(qname = (TEXTNS,'image-count'), **args)
|
||||||
|
|
||||||
|
def IndexBody(**args):
|
||||||
|
return Element(qname = (TEXTNS,'index-body'), **args)
|
||||||
|
|
||||||
|
def IndexEntryBibliography(**args):
|
||||||
|
return Element(qname = (TEXTNS,'index-entry-bibliography'), **args)
|
||||||
|
|
||||||
|
def IndexEntryChapter(**args):
|
||||||
|
return Element(qname = (TEXTNS,'index-entry-chapter'), **args)
|
||||||
|
|
||||||
|
def IndexEntryLinkEnd(**args):
|
||||||
|
return Element(qname = (TEXTNS,'index-entry-link-end'), **args)
|
||||||
|
|
||||||
|
def IndexEntryLinkStart(**args):
|
||||||
|
return Element(qname = (TEXTNS,'index-entry-link-start'), **args)
|
||||||
|
|
||||||
|
def IndexEntryPageNumber(**args):
|
||||||
|
return Element(qname = (TEXTNS,'index-entry-page-number'), **args)
|
||||||
|
|
||||||
|
def IndexEntrySpan(**args):
|
||||||
|
return Element(qname = (TEXTNS,'index-entry-span'), **args)
|
||||||
|
|
||||||
|
def IndexEntryTabStop(**args):
|
||||||
|
return Element(qname = (TEXTNS,'index-entry-tab-stop'), **args)
|
||||||
|
|
||||||
|
def IndexEntryText(**args):
|
||||||
|
return Element(qname = (TEXTNS,'index-entry-text'), **args)
|
||||||
|
|
||||||
|
def IndexSourceStyle(**args):
|
||||||
|
return Element(qname = (TEXTNS,'index-source-style'), **args)
|
||||||
|
|
||||||
|
def IndexSourceStyles(**args):
|
||||||
|
return Element(qname = (TEXTNS,'index-source-styles'), **args)
|
||||||
|
|
||||||
|
def IndexTitle(**args):
|
||||||
|
return Element(qname = (TEXTNS,'index-title'), **args)
|
||||||
|
|
||||||
|
def IndexTitleTemplate(**args):
|
||||||
|
return Element(qname = (TEXTNS,'index-title-template'), **args)
|
||||||
|
|
||||||
|
def InitialCreator(**args):
|
||||||
|
return Element(qname = (TEXTNS,'initial-creator'), **args)
|
||||||
|
|
||||||
|
def Insertion(**args):
|
||||||
|
return Element(qname = (TEXTNS,'insertion'), **args)
|
||||||
|
|
||||||
|
def Keywords(**args):
|
||||||
|
return Element(qname = (TEXTNS,'keywords'), **args)
|
||||||
|
|
||||||
|
def LineBreak(**args):
|
||||||
|
return Element(qname = (TEXTNS,'line-break'), **args)
|
||||||
|
|
||||||
|
def LinenumberingConfiguration(**args):
|
||||||
|
return Element(qname = (TEXTNS,'linenumbering-configuration'), **args)
|
||||||
|
|
||||||
|
def LinenumberingSeparator(**args):
|
||||||
|
return Element(qname = (TEXTNS,'linenumbering-separator'), **args)
|
||||||
|
|
||||||
|
def List(**args):
|
||||||
|
return Element(qname = (TEXTNS,'list'), **args)
|
||||||
|
|
||||||
|
def ListHeader(**args):
|
||||||
|
return Element(qname = (TEXTNS,'list-header'), **args)
|
||||||
|
|
||||||
|
def ListItem(**args):
|
||||||
|
return Element(qname = (TEXTNS,'list-item'), **args)
|
||||||
|
|
||||||
|
def ListLevelStyleBullet(**args):
|
||||||
|
return Element(qname = (TEXTNS,'list-level-style-bullet'), **args)
|
||||||
|
|
||||||
|
def ListLevelStyleImage(**args):
|
||||||
|
return Element(qname = (TEXTNS,'list-level-style-image'), **args)
|
||||||
|
|
||||||
|
def ListLevelStyleNumber(**args):
|
||||||
|
return Element(qname = (TEXTNS,'list-level-style-number'), **args)
|
||||||
|
|
||||||
|
def ListStyle(**args):
|
||||||
|
return StyleElement(qname = (TEXTNS,'list-style'), **args)
|
||||||
|
|
||||||
|
def Measure(**args):
|
||||||
|
return Element(qname = (TEXTNS,'measure'), **args)
|
||||||
|
|
||||||
|
def ModificationDate(**args):
|
||||||
|
return Element(qname = (TEXTNS,'modification-date'), **args)
|
||||||
|
|
||||||
|
def ModificationTime(**args):
|
||||||
|
return Element(qname = (TEXTNS,'modification-time'), **args)
|
||||||
|
|
||||||
|
def Note(**args):
|
||||||
|
return Element(qname = (TEXTNS,'note'), **args)
|
||||||
|
|
||||||
|
def NoteBody(**args):
|
||||||
|
return Element(qname = (TEXTNS,'note-body'), **args)
|
||||||
|
|
||||||
|
def NoteCitation(**args):
|
||||||
|
return Element(qname = (TEXTNS,'note-citation'), **args)
|
||||||
|
|
||||||
|
def NoteContinuationNoticeBackward(**args):
|
||||||
|
return Element(qname = (TEXTNS,'note-continuation-notice-backward'), **args)
|
||||||
|
|
||||||
|
def NoteContinuationNoticeForward(**args):
|
||||||
|
return Element(qname = (TEXTNS,'note-continuation-notice-forward'), **args)
|
||||||
|
|
||||||
|
def NoteRef(**args):
|
||||||
|
return Element(qname = (TEXTNS,'note-ref'), **args)
|
||||||
|
|
||||||
|
def NotesConfiguration(**args):
|
||||||
|
return Element(qname = (TEXTNS,'notes-configuration'), **args)
|
||||||
|
|
||||||
|
def Number(**args):
|
||||||
|
return Element(qname = (TEXTNS,'number'), **args)
|
||||||
|
|
||||||
|
def NumberedParagraph(**args):
|
||||||
|
return Element(qname = (TEXTNS,'numbered-paragraph'), **args)
|
||||||
|
|
||||||
|
def ObjectCount(**args):
|
||||||
|
return Element(qname = (TEXTNS,'object-count'), **args)
|
||||||
|
|
||||||
|
def ObjectIndex(**args):
|
||||||
|
return Element(qname = (TEXTNS,'object-index'), **args)
|
||||||
|
|
||||||
|
def ObjectIndexEntryTemplate(**args):
|
||||||
|
return Element(qname = (TEXTNS,'object-index-entry-template'), **args)
|
||||||
|
|
||||||
|
def ObjectIndexSource(**args):
|
||||||
|
return Element(qname = (TEXTNS,'object-index-source'), **args)
|
||||||
|
|
||||||
|
def OutlineLevelStyle(**args):
|
||||||
|
return Element(qname = (TEXTNS,'outline-level-style'), **args)
|
||||||
|
|
||||||
|
def OutlineStyle(**args):
|
||||||
|
return Element(qname = (TEXTNS,'outline-style'), **args)
|
||||||
|
|
||||||
|
def P(**args):
|
||||||
|
return Element(qname = (TEXTNS, 'p'), **args)
|
||||||
|
|
||||||
|
def Page(**args):
|
||||||
|
return Element(qname = (TEXTNS,'page'), **args)
|
||||||
|
|
||||||
|
def PageContinuation(**args):
|
||||||
|
return Element(qname = (TEXTNS,'page-continuation'), **args)
|
||||||
|
|
||||||
|
def PageCount(**args):
|
||||||
|
return Element(qname = (TEXTNS,'page-count'), **args)
|
||||||
|
|
||||||
|
def PageNumber(**args):
|
||||||
|
return Element(qname = (TEXTNS,'page-number'), **args)
|
||||||
|
|
||||||
|
def PageSequence(**args):
|
||||||
|
return Element(qname = (TEXTNS,'page-sequence'), **args)
|
||||||
|
|
||||||
|
def PageVariableGet(**args):
|
||||||
|
return Element(qname = (TEXTNS,'page-variable-get'), **args)
|
||||||
|
|
||||||
|
def PageVariableSet(**args):
|
||||||
|
return Element(qname = (TEXTNS,'page-variable-set'), **args)
|
||||||
|
|
||||||
|
def ParagraphCount(**args):
|
||||||
|
return Element(qname = (TEXTNS,'paragraph-count'), **args)
|
||||||
|
|
||||||
|
def Placeholder(**args):
|
||||||
|
return Element(qname = (TEXTNS,'placeholder'), **args)
|
||||||
|
|
||||||
|
def PrintDate(**args):
|
||||||
|
return Element(qname = (TEXTNS,'print-date'), **args)
|
||||||
|
|
||||||
|
def PrintTime(**args):
|
||||||
|
return Element(qname = (TEXTNS,'print-time'), **args)
|
||||||
|
|
||||||
|
def PrintedBy(**args):
|
||||||
|
return Element(qname = (TEXTNS,'printed-by'), **args)
|
||||||
|
|
||||||
|
def ReferenceMark(**args):
|
||||||
|
return Element(qname = (TEXTNS,'reference-mark'), **args)
|
||||||
|
|
||||||
|
def ReferenceMarkEnd(**args):
|
||||||
|
return Element(qname = (TEXTNS,'reference-mark-end'), **args)
|
||||||
|
|
||||||
|
def ReferenceMarkStart(**args):
|
||||||
|
return Element(qname = (TEXTNS,'reference-mark-start'), **args)
|
||||||
|
|
||||||
|
def ReferenceRef(**args):
|
||||||
|
return Element(qname = (TEXTNS,'reference-ref'), **args)
|
||||||
|
|
||||||
|
def Ruby(**args):
|
||||||
|
return Element(qname = (TEXTNS,'ruby'), **args)
|
||||||
|
|
||||||
|
def RubyBase(**args):
|
||||||
|
return Element(qname = (TEXTNS,'ruby-base'), **args)
|
||||||
|
|
||||||
|
def RubyText(**args):
|
||||||
|
return Element(qname = (TEXTNS,'ruby-text'), **args)
|
||||||
|
|
||||||
|
def S(**args):
|
||||||
|
return Element(qname = (TEXTNS,'s'), **args)
|
||||||
|
|
||||||
|
def Script(**args):
|
||||||
|
return Element(qname = (TEXTNS,'script'), **args)
|
||||||
|
|
||||||
|
def Section(**args):
|
||||||
|
return Element(qname = (TEXTNS,'section'), **args)
|
||||||
|
|
||||||
|
def SectionSource(**args):
|
||||||
|
return Element(qname = (TEXTNS,'section-source'), **args)
|
||||||
|
|
||||||
|
def SenderCity(**args):
|
||||||
|
return Element(qname = (TEXTNS,'sender-city'), **args)
|
||||||
|
|
||||||
|
def SenderCompany(**args):
|
||||||
|
return Element(qname = (TEXTNS,'sender-company'), **args)
|
||||||
|
|
||||||
|
def SenderCountry(**args):
|
||||||
|
return Element(qname = (TEXTNS,'sender-country'), **args)
|
||||||
|
|
||||||
|
def SenderEmail(**args):
|
||||||
|
return Element(qname = (TEXTNS,'sender-email'), **args)
|
||||||
|
|
||||||
|
def SenderFax(**args):
|
||||||
|
return Element(qname = (TEXTNS,'sender-fax'), **args)
|
||||||
|
|
||||||
|
def SenderFirstname(**args):
|
||||||
|
return Element(qname = (TEXTNS,'sender-firstname'), **args)
|
||||||
|
|
||||||
|
def SenderInitials(**args):
|
||||||
|
return Element(qname = (TEXTNS,'sender-initials'), **args)
|
||||||
|
|
||||||
|
def SenderLastname(**args):
|
||||||
|
return Element(qname = (TEXTNS,'sender-lastname'), **args)
|
||||||
|
|
||||||
|
def SenderPhonePrivate(**args):
|
||||||
|
return Element(qname = (TEXTNS,'sender-phone-private'), **args)
|
||||||
|
|
||||||
|
def SenderPhoneWork(**args):
|
||||||
|
return Element(qname = (TEXTNS,'sender-phone-work'), **args)
|
||||||
|
|
||||||
|
def SenderPosition(**args):
|
||||||
|
return Element(qname = (TEXTNS,'sender-position'), **args)
|
||||||
|
|
||||||
|
def SenderPostalCode(**args):
|
||||||
|
return Element(qname = (TEXTNS,'sender-postal-code'), **args)
|
||||||
|
|
||||||
|
def SenderStateOrProvince(**args):
|
||||||
|
return Element(qname = (TEXTNS,'sender-state-or-province'), **args)
|
||||||
|
|
||||||
|
def SenderStreet(**args):
|
||||||
|
return Element(qname = (TEXTNS,'sender-street'), **args)
|
||||||
|
|
||||||
|
def SenderTitle(**args):
|
||||||
|
return Element(qname = (TEXTNS,'sender-title'), **args)
|
||||||
|
|
||||||
|
def Sequence(**args):
|
||||||
|
return Element(qname = (TEXTNS,'sequence'), **args)
|
||||||
|
|
||||||
|
def SequenceDecl(**args):
|
||||||
|
return Element(qname = (TEXTNS,'sequence-decl'), **args)
|
||||||
|
|
||||||
|
def SequenceDecls(**args):
|
||||||
|
return Element(qname = (TEXTNS,'sequence-decls'), **args)
|
||||||
|
|
||||||
|
def SequenceRef(**args):
|
||||||
|
return Element(qname = (TEXTNS,'sequence-ref'), **args)
|
||||||
|
|
||||||
|
def SheetName(**args):
|
||||||
|
return Element(qname = (TEXTNS,'sheet-name'), **args)
|
||||||
|
|
||||||
|
def SortKey(**args):
|
||||||
|
return Element(qname = (TEXTNS,'sort-key'), **args)
|
||||||
|
|
||||||
|
def Span(**args):
|
||||||
|
return Element(qname = (TEXTNS,'span'), **args)
|
||||||
|
|
||||||
|
def Subject(**args):
|
||||||
|
return Element(qname = (TEXTNS,'subject'), **args)
|
||||||
|
|
||||||
|
def Tab(**args):
|
||||||
|
return Element(qname = (TEXTNS,'tab'), **args)
|
||||||
|
|
||||||
|
def TableCount(**args):
|
||||||
|
return Element(qname = (TEXTNS,'table-count'), **args)
|
||||||
|
|
||||||
|
def TableFormula(**args):
|
||||||
|
return Element(qname = (TEXTNS,'table-formula'), **args)
|
||||||
|
|
||||||
|
def TableIndex(**args):
|
||||||
|
return Element(qname = (TEXTNS,'table-index'), **args)
|
||||||
|
|
||||||
|
def TableIndexEntryTemplate(**args):
|
||||||
|
return Element(qname = (TEXTNS,'table-index-entry-template'), **args)
|
||||||
|
|
||||||
|
def TableIndexSource(**args):
|
||||||
|
return Element(qname = (TEXTNS,'table-index-source'), **args)
|
||||||
|
|
||||||
|
def TableOfContent(**args):
|
||||||
|
return Element(qname = (TEXTNS,'table-of-content'), **args)
|
||||||
|
|
||||||
|
def TableOfContentEntryTemplate(**args):
|
||||||
|
return Element(qname = (TEXTNS,'table-of-content-entry-template'), **args)
|
||||||
|
|
||||||
|
def TableOfContentSource(**args):
|
||||||
|
return Element(qname = (TEXTNS,'table-of-content-source'), **args)
|
||||||
|
|
||||||
|
def TemplateName(**args):
|
||||||
|
return Element(qname = (TEXTNS,'template-name'), **args)
|
||||||
|
|
||||||
|
def TextInput(**args):
|
||||||
|
return Element(qname = (TEXTNS,'text-input'), **args)
|
||||||
|
|
||||||
|
def Time(**args):
|
||||||
|
return Element(qname = (TEXTNS,'time'), **args)
|
||||||
|
|
||||||
|
def Title(**args):
|
||||||
|
return Element(qname = (TEXTNS,'title'), **args)
|
||||||
|
|
||||||
|
def TocMark(**args):
|
||||||
|
return Element(qname = (TEXTNS,'toc-mark'), **args)
|
||||||
|
|
||||||
|
def TocMarkEnd(**args):
|
||||||
|
return Element(qname = (TEXTNS,'toc-mark-end'), **args)
|
||||||
|
|
||||||
|
def TocMarkStart(**args):
|
||||||
|
return Element(qname = (TEXTNS,'toc-mark-start'), **args)
|
||||||
|
|
||||||
|
def TrackedChanges(**args):
|
||||||
|
return Element(qname = (TEXTNS,'tracked-changes'), **args)
|
||||||
|
|
||||||
|
def UserDefined(**args):
|
||||||
|
return Element(qname = (TEXTNS,'user-defined'), **args)
|
||||||
|
|
||||||
|
def UserFieldDecl(**args):
|
||||||
|
return Element(qname = (TEXTNS,'user-field-decl'), **args)
|
||||||
|
|
||||||
|
def UserFieldDecls(**args):
|
||||||
|
return Element(qname = (TEXTNS,'user-field-decls'), **args)
|
||||||
|
|
||||||
|
def UserFieldGet(**args):
|
||||||
|
return Element(qname = (TEXTNS,'user-field-get'), **args)
|
||||||
|
|
||||||
|
def UserFieldInput(**args):
|
||||||
|
return Element(qname = (TEXTNS,'user-field-input'), **args)
|
||||||
|
|
||||||
|
def UserIndex(**args):
|
||||||
|
return Element(qname = (TEXTNS,'user-index'), **args)
|
||||||
|
|
||||||
|
def UserIndexEntryTemplate(**args):
|
||||||
|
return Element(qname = (TEXTNS,'user-index-entry-template'), **args)
|
||||||
|
|
||||||
|
def UserIndexMark(**args):
|
||||||
|
return Element(qname = (TEXTNS,'user-index-mark'), **args)
|
||||||
|
|
||||||
|
def UserIndexMarkEnd(**args):
|
||||||
|
return Element(qname = (TEXTNS,'user-index-mark-end'), **args)
|
||||||
|
|
||||||
|
def UserIndexMarkStart(**args):
|
||||||
|
return Element(qname = (TEXTNS,'user-index-mark-start'), **args)
|
||||||
|
|
||||||
|
def UserIndexSource(**args):
|
||||||
|
return Element(qname = (TEXTNS,'user-index-source'), **args)
|
||||||
|
|
||||||
|
def VariableDecl(**args):
|
||||||
|
return Element(qname = (TEXTNS,'variable-decl'), **args)
|
||||||
|
|
||||||
|
def VariableDecls(**args):
|
||||||
|
return Element(qname = (TEXTNS,'variable-decls'), **args)
|
||||||
|
|
||||||
|
def VariableGet(**args):
|
||||||
|
return Element(qname = (TEXTNS,'variable-get'), **args)
|
||||||
|
|
||||||
|
def VariableInput(**args):
|
||||||
|
return Element(qname = (TEXTNS,'variable-input'), **args)
|
||||||
|
|
||||||
|
def VariableSet(**args):
|
||||||
|
return Element(qname = (TEXTNS,'variable-set'), **args)
|
||||||
|
|
||||||
|
def WordCount(**args):
|
||||||
|
return Element(qname = (TEXTNS,'word-count'), **args)
|
||||||
|
|
427
src/odf/thumbnail.py
Normal file
427
src/odf/thumbnail.py
Normal file
@ -0,0 +1,427 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# This contains a 128x128 px thumbnail in PNG format
|
||||||
|
# Taken from http://www.zwahlendesign.ch/en/node/20
|
||||||
|
# openoffice_icons/openoffice_icons_linux/openoffice11.png
|
||||||
|
# License: Freeware
|
||||||
|
import base64
|
||||||
|
|
||||||
|
iconstr = """\
|
||||||
|
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
|
||||||
|
N38FAAAAAElFTkSuQmCC\
|
||||||
|
"""
|
||||||
|
|
||||||
|
def thumbnail():
|
||||||
|
icon = base64.decodestring(iconstr)
|
||||||
|
return icon
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
icon = thumbnail()
|
||||||
|
f = file("thumbnail.png","wb")
|
||||||
|
f.write(icon)
|
||||||
|
f.close()
|
322
src/odf/userfield.py
Normal file
322
src/odf/userfield.py
Normal file
@ -0,0 +1,322 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
|
||||||
|
#
|
||||||
|
# This is free software. You may redistribute it under the terms
|
||||||
|
# of the Apache license and the GNU General Public License Version
|
||||||
|
# 2 or at your option any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public
|
||||||
|
# License along with this program; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
#
|
||||||
|
# Contributor(s): Michael Howitz, gocept gmbh & co. kg
|
||||||
|
#
|
||||||
|
# $Id: userfield.py 447 2008-07-10 20:01:30Z roug $
|
||||||
|
|
||||||
|
"""Class to show and manipulate user fields in odf documents."""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import zipfile
|
||||||
|
|
||||||
|
import xml.sax
|
||||||
|
import xml.sax.handler
|
||||||
|
import xml.sax.saxutils
|
||||||
|
|
||||||
|
from odf.namespaces import OFFICENS, TEXTNS
|
||||||
|
|
||||||
|
try:
|
||||||
|
from cStringIO import StringIO
|
||||||
|
except ImportError:
|
||||||
|
from StringIO import StringIO
|
||||||
|
|
||||||
|
|
||||||
|
OUTENCODING = "utf-8"
|
||||||
|
|
||||||
|
|
||||||
|
# OpenDocument v.1.0 section 6.7.1
|
||||||
|
VALUE_TYPES = {
|
||||||
|
'float': (OFFICENS, u'value'),
|
||||||
|
'percentage': (OFFICENS, u'value'),
|
||||||
|
'currency': (OFFICENS, u'value'),
|
||||||
|
'date': (OFFICENS, u'date-value'),
|
||||||
|
'time': (OFFICENS, u'time-value'),
|
||||||
|
'boolean': (OFFICENS, u'boolean-value'),
|
||||||
|
'string': (OFFICENS, u'string-value'),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class UserFields(object):
|
||||||
|
"""List, view and manipulate user fields."""
|
||||||
|
|
||||||
|
# these attributes can be a filename or a file like object
|
||||||
|
src_file = None
|
||||||
|
dest_file = None
|
||||||
|
|
||||||
|
def __init__(self, src=None, dest=None):
|
||||||
|
"""Constructor
|
||||||
|
|
||||||
|
src ... source document name, file like object or None for stdin
|
||||||
|
dest ... destination document name, file like object or None for stdout
|
||||||
|
|
||||||
|
"""
|
||||||
|
self.src_file = src
|
||||||
|
self.dest_file = dest
|
||||||
|
|
||||||
|
def list_fields(self):
|
||||||
|
"""List (extract) all known user-fields.
|
||||||
|
|
||||||
|
Returns list of user-field names.
|
||||||
|
|
||||||
|
"""
|
||||||
|
return [x[0] for x in self.list_fields_and_values()]
|
||||||
|
|
||||||
|
def list_fields_and_values(self, field_names=None):
|
||||||
|
"""List (extract) user-fields with type and value.
|
||||||
|
|
||||||
|
field_names ... list of field names to show or None for all.
|
||||||
|
|
||||||
|
Returns list of tuples (<field name>, <field type>, <value>).
|
||||||
|
|
||||||
|
"""
|
||||||
|
found_fields = []
|
||||||
|
def _callback(field_name, value_type, value, attrs):
|
||||||
|
if field_names is None or field_name in field_names:
|
||||||
|
found_fields.append((field_name.encode(OUTENCODING),
|
||||||
|
value_type.encode(OUTENCODING),
|
||||||
|
value.encode(OUTENCODING)))
|
||||||
|
return attrs
|
||||||
|
|
||||||
|
self._content_handler(_callback)
|
||||||
|
return found_fields
|
||||||
|
|
||||||
|
def list_values(self, field_names):
|
||||||
|
"""Extract the contents of given field names from the file.
|
||||||
|
|
||||||
|
field_names ... list of field names
|
||||||
|
|
||||||
|
Returns list of field values.
|
||||||
|
|
||||||
|
"""
|
||||||
|
return [x[2] for x in self.list_fields_and_values(field_names)]
|
||||||
|
|
||||||
|
def get(self, field_name):
|
||||||
|
"""Extract the contents of this field from the file.
|
||||||
|
|
||||||
|
Returns field value or None if field does not exist.
|
||||||
|
|
||||||
|
"""
|
||||||
|
values = self.list_values([field_name])
|
||||||
|
if not values:
|
||||||
|
return None
|
||||||
|
return values[0]
|
||||||
|
|
||||||
|
def get_type_and_value(self, field_name):
|
||||||
|
"""Extract the type and contents of this field from the file.
|
||||||
|
|
||||||
|
Returns tuple (<type>, <field-value>) or None if field does not exist.
|
||||||
|
|
||||||
|
"""
|
||||||
|
fields = self.list_fields_and_values([field_name])
|
||||||
|
if not fields:
|
||||||
|
return None
|
||||||
|
field_name, value_type, value = fields[0]
|
||||||
|
return value_type, value
|
||||||
|
|
||||||
|
def update(self, data):
|
||||||
|
"""Set the value of user fields. The field types will be the same.
|
||||||
|
|
||||||
|
data ... dict, with field name as key, field value as value
|
||||||
|
|
||||||
|
Returns None
|
||||||
|
|
||||||
|
"""
|
||||||
|
def _callback(field_name, value_type, value, attrs):
|
||||||
|
if field_name in data:
|
||||||
|
valattr = VALUE_TYPES.get(value_type)
|
||||||
|
attrs = dict(attrs.items())
|
||||||
|
# Take advantage that startElementNS can take a normal
|
||||||
|
# dict as attrs
|
||||||
|
attrs[valattr] = data[field_name]
|
||||||
|
return attrs
|
||||||
|
self._content_handler(_callback, write_file=True)
|
||||||
|
|
||||||
|
def _content_handler(self, callback_func, write_file=False):
|
||||||
|
"""Handle the content using the callback function and write result if
|
||||||
|
necessary.
|
||||||
|
|
||||||
|
callback_func ... function called for each field found in odf document
|
||||||
|
signature: field_name ... name of current field
|
||||||
|
value_type ... type of current field
|
||||||
|
value ... value of current field
|
||||||
|
attrs ... tuple of attrs of current field
|
||||||
|
returns: tuple or dict of attrs
|
||||||
|
write_file ... boolean telling wether write result to file
|
||||||
|
|
||||||
|
"""
|
||||||
|
class DevNull(object):
|
||||||
|
"""IO-object which behaves like /dev/null."""
|
||||||
|
def write(self, str):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# get input
|
||||||
|
if isinstance(self.src_file, basestring):
|
||||||
|
# src_file is a filename, check if it is a zip-file
|
||||||
|
if not zipfile.is_zipfile(self.src_file):
|
||||||
|
raise TypeError("%s is no odt file." % self.src_file)
|
||||||
|
elif self.src_file is None:
|
||||||
|
# use stdin if no file given
|
||||||
|
self.src_file = sys.stdin
|
||||||
|
|
||||||
|
zin = zipfile.ZipFile(self.src_file, 'r')
|
||||||
|
content_xml = zin.read('content.xml')
|
||||||
|
|
||||||
|
# prepare output
|
||||||
|
if write_file:
|
||||||
|
output_io = StringIO()
|
||||||
|
if self.dest_file is None:
|
||||||
|
# use stdout if no filename given
|
||||||
|
self.dest_file = sys.stdout
|
||||||
|
zout = zipfile.ZipFile(self.dest_file, 'w')
|
||||||
|
else:
|
||||||
|
output_io = DevNull()
|
||||||
|
|
||||||
|
|
||||||
|
# parse input
|
||||||
|
odfs = ODFContentParser(callback_func, output_io)
|
||||||
|
parser = xml.sax.make_parser()
|
||||||
|
parser.setFeature(xml.sax.handler.feature_namespaces, 1)
|
||||||
|
parser.setContentHandler(odfs)
|
||||||
|
parser.parse(StringIO(content_xml))
|
||||||
|
|
||||||
|
# write output
|
||||||
|
if write_file:
|
||||||
|
# Loop through the input zipfile and copy the content to
|
||||||
|
# the output until we get to the content.xml. Then
|
||||||
|
# substitute.
|
||||||
|
for zinfo in zin.infolist():
|
||||||
|
if zinfo.filename == "content.xml":
|
||||||
|
# Write meta
|
||||||
|
zi = zipfile.ZipInfo("content.xml", time.localtime()[:6])
|
||||||
|
zi.compress_type = zipfile.ZIP_DEFLATED
|
||||||
|
zout.writestr(zi, odfs.content())
|
||||||
|
else:
|
||||||
|
payload = zin.read(zinfo.filename)
|
||||||
|
zout.writestr(zinfo, payload)
|
||||||
|
zout.close()
|
||||||
|
zin.close()
|
||||||
|
|
||||||
|
|
||||||
|
class ODFContentParser(xml.sax.saxutils.XMLGenerator):
|
||||||
|
|
||||||
|
def __init__(self, callback_func, out=None, encoding=OUTENCODING):
|
||||||
|
"""Constructor.
|
||||||
|
|
||||||
|
callback_func ... function called for each field found in odf document
|
||||||
|
signature: field_name ... name of current field
|
||||||
|
value_type ... type of current field
|
||||||
|
value ... value of current field
|
||||||
|
attrs ... tuple of attrs of current field
|
||||||
|
returns: tuple or dict of attrs
|
||||||
|
out ... file like object for output
|
||||||
|
encoding ... encoding for output
|
||||||
|
|
||||||
|
"""
|
||||||
|
self._callback_func = callback_func
|
||||||
|
xml.sax.saxutils.XMLGenerator.__init__(self, out, encoding)
|
||||||
|
|
||||||
|
def startElementNS(self, name, qname, attrs):
|
||||||
|
if name == (TEXTNS, u'user-field-decl'):
|
||||||
|
field_name = attrs.get((TEXTNS, u'name'))
|
||||||
|
value_type = attrs.get((OFFICENS, u'value-type'))
|
||||||
|
if value_type == 'string':
|
||||||
|
value = attrs.get((OFFICENS, u'string-value'))
|
||||||
|
else:
|
||||||
|
value = attrs.get((OFFICENS, u'value'))
|
||||||
|
|
||||||
|
attrs = self._callback_func(field_name, value_type, value, attrs)
|
||||||
|
|
||||||
|
self._startElementNS(name, qname, attrs)
|
||||||
|
|
||||||
|
def _startElementNS(self, name, qname, attrs):
|
||||||
|
# copy of xml.sax.saxutils.XMLGenerator.startElementNS
|
||||||
|
# necessary because we have to provide our own writeattr
|
||||||
|
# function which is called by this method
|
||||||
|
if name[0] is None:
|
||||||
|
name = name[1]
|
||||||
|
elif self._current_context[name[0]] is None:
|
||||||
|
# default namespace
|
||||||
|
name = name[1]
|
||||||
|
else:
|
||||||
|
name = self._current_context[name[0]] + ":" + name[1]
|
||||||
|
self._out.write('<' + name)
|
||||||
|
|
||||||
|
for k,v in self._undeclared_ns_maps:
|
||||||
|
if k is None:
|
||||||
|
self._out.write(' xmlns="%s"' % (v or ''))
|
||||||
|
else:
|
||||||
|
self._out.write(' xmlns:%s="%s"' % (k,v))
|
||||||
|
self._undeclared_ns_maps = []
|
||||||
|
|
||||||
|
for (name, value) in attrs.items():
|
||||||
|
if name[0] is None:
|
||||||
|
name = name[1]
|
||||||
|
elif self._current_context[name[0]] is None:
|
||||||
|
# default namespace
|
||||||
|
#If an attribute has a nsuri but not a prefix, we must
|
||||||
|
#create a prefix and add a nsdecl
|
||||||
|
prefix = self.GENERATED_PREFIX % self._generated_prefix_ctr
|
||||||
|
self._generated_prefix_ctr = self._generated_prefix_ctr + 1
|
||||||
|
name = prefix + ':' + name[1]
|
||||||
|
self._out.write(' xmlns:%s=%s' % (prefix, quoteattr(name[0])))
|
||||||
|
self._current_context[name[0]] = prefix
|
||||||
|
else:
|
||||||
|
name = self._current_context[name[0]] + ":" + name[1]
|
||||||
|
self._out.write(' %s=' % name)
|
||||||
|
writeattr(self._out, value)
|
||||||
|
self._out.write('>')
|
||||||
|
|
||||||
|
def content(self):
|
||||||
|
return self._out.getvalue()
|
||||||
|
|
||||||
|
|
||||||
|
ATTR_ENTITIES = {
|
||||||
|
'\n': '
' # convert newlines into entities inside attributes
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def writetext(stream, text, entities={}):
|
||||||
|
text = xml.sax.saxutils.escape(text, entities)
|
||||||
|
try:
|
||||||
|
stream.write(text)
|
||||||
|
except UnicodeError:
|
||||||
|
for c in text:
|
||||||
|
try:
|
||||||
|
stream.write(c)
|
||||||
|
except UnicodeError:
|
||||||
|
stream.write(u"&#%d;" % ord(c))
|
||||||
|
|
||||||
|
def writeattr(stream, text):
|
||||||
|
# copied from xml.sax.saxutils.writeattr added support for an
|
||||||
|
# additional entity mapping
|
||||||
|
countdouble = text.count('"')
|
||||||
|
entities = ATTR_ENTITIES.copy()
|
||||||
|
if countdouble:
|
||||||
|
countsingle = text.count("'")
|
||||||
|
if countdouble <= countsingle:
|
||||||
|
entities['"'] = """
|
||||||
|
quote = '"'
|
||||||
|
else:
|
||||||
|
entities["'"] = "'"
|
||||||
|
quote = "'"
|
||||||
|
else:
|
||||||
|
quote = '"'
|
||||||
|
stream.write(quote)
|
||||||
|
writetext(stream, text, entities)
|
||||||
|
stream.write(quote)
|
29
src/odf/xforms.py
Normal file
29
src/odf/xforms.py
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
|
||||||
|
#
|
||||||
|
# This library is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU Lesser General Public
|
||||||
|
# License as published by the Free Software Foundation; either
|
||||||
|
# version 2.1 of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This library is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
# Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public
|
||||||
|
# License along with this library; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
#
|
||||||
|
# Contributor(s):
|
||||||
|
#
|
||||||
|
|
||||||
|
from namespaces import XFORMSNS
|
||||||
|
from element import Element
|
||||||
|
|
||||||
|
# ODF 1.0 section 11.2
|
||||||
|
# XForms is designed to be embedded in another XML format.
|
||||||
|
# Autogenerated
|
||||||
|
def Model(**args):
|
||||||
|
return Element(qname = (XFORMSNS,'model'), **args)
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user