Sync to trunk

This commit is contained in:
John Schember 2009-01-30 18:32:41 -05:00
commit 6fd9510f19
32 changed files with 15206 additions and 13778 deletions

View File

@ -424,15 +424,19 @@ def entity_to_unicode(match, exceptions=[], encoding='cp1252'):
if isosx: if isosx:
fdir = os.path.expanduser('~/.fonts') fdir = os.path.expanduser('~/.fonts')
if not os.path.exists(fdir): try:
os.makedirs(fdir) if not os.path.exists(fdir):
if not os.path.exists(os.path.join(fdir, 'LiberationSans_Regular.ttf')): os.makedirs(fdir)
from calibre.ebooks.lrf.fonts.liberation import __all__ as fonts if not os.path.exists(os.path.join(fdir, 'LiberationSans_Regular.ttf')):
for font in fonts: from calibre.ebooks.lrf.fonts.liberation import __all__ as fonts
l = {} for font in fonts:
exec 'from calibre.ebooks.lrf.fonts.liberation.'+font+' import font_data' in l l = {}
open(os.path.join(fdir, font+'.ttf'), 'wb').write(l['font_data']) exec 'from calibre.ebooks.lrf.fonts.liberation.'+font+' import font_data' in l
open(os.path.join(fdir, font+'.ttf'), 'wb').write(l['font_data'])
except:
import traceback
traceback.print_exc()
# Migrate from QSettings based config system # Migrate from QSettings based config system
from calibre.utils.config import migrate from calibre.utils.config import migrate
migrate() migrate()

View File

@ -898,7 +898,7 @@ def config(defaults=None, config_name='html',
metadata('title', ['-t', '--title'], default=None, metadata('title', ['-t', '--title'], default=None,
help=_('Set the title. Default is to autodetect.')) help=_('Set the title. Default is to autodetect.'))
metadata('authors', ['-a', '--authors'], default=None, metadata('authors', ['-a', '--authors'], default=None,
help=_('The author(s) of the ebook, as a comma separated list.')) help=_('The author(s) of the ebook, as a & separated list.'))
metadata('tags', ['--subjects'], default=None, metadata('tags', ['--subjects'], default=None,
help=_('The subject(s) of this book, as a comma separated list.')) help=_('The subject(s) of this book, as a comma separated list.'))
metadata('publisher', ['--publisher'], default=None, metadata('publisher', ['--publisher'], default=None,
@ -994,7 +994,9 @@ def merge_metadata(htmlfile, opf, opts):
val = getattr(opts, attr, None) val = getattr(opts, attr, None)
if val is None or val == _('Unknown') or val == [_('Unknown')]: if val is None or val == _('Unknown') or val == [_('Unknown')]:
continue continue
if attr in ('authors', 'tags'): if attr =='authors':
val = [i.strip() for i in val.split('&') if i.strip()]
elif attr == 'tags':
val = [i.strip() for i in val.split(',') if i.strip()] val = [i.strip() for i in val.split(',') if i.strip()]
setattr(mi, attr, val) setattr(mi, attr, val)

View File

@ -170,7 +170,7 @@ def generate_html(rtfpath, tdir):
f.write(res) f.write(res)
f.close() f.close()
try: try:
mi = get_metadata(open(rtfpath, 'rb')) mi = get_metadata(open(rtfpath, 'rb'), 'rtf')
except: except:
mi = MetaInformation(None, None) mi = MetaInformation(None, None)
if not mi.title: if not mi.title:

View File

@ -16,9 +16,9 @@
<dc:description py:if="mi.comments">${mi.comments}</dc:description> <dc:description py:if="mi.comments">${mi.comments}</dc:description>
<dc:publisher py:if="mi.publisher">${mi.publisher}</dc:publisher> <dc:publisher py:if="mi.publisher">${mi.publisher}</dc:publisher>
<dc:identifier opf:scheme="ISBN" py:if="mi.isbn">${mi.isbn}</dc:identifier> <dc:identifier opf:scheme="ISBN" py:if="mi.isbn">${mi.isbn}</dc:identifier>
<series py:if="mi.series">${mi.series}</series> <meta py:if="mi.series is not None" name="series" content="${mi.series}"/>
<series_index py:if="mi.series_index is not None">${mi.series_index}</series_index> <meta py:if="mi.series_index is not None" name="series_index" content="${mi.series_index}"/>
<rating py:if="mi.rating is not None">${mi.rating}</rating> <meta py:if="mi.rating is not None" name="rating" content="${mi.rating}"/>
<py:for each="tag in mi.tags"> <py:for each="tag in mi.tags">
<dc:subject py:if="mi.tags is not None">${tag}</dc:subject> <dc:subject py:if="mi.tags is not None">${tag}</dc:subject>
</py:for> </py:for>

View File

@ -392,8 +392,8 @@ class MetadataField(object):
def __set__(self, obj, val): def __set__(self, obj, val):
elem = obj.get_metadata_element(self.name) elem = obj.get_metadata_element(self.name)
if elem is None: if elem is None:
elem = obj.create_metadata_element(self.name, ns='dc' if self.is_dc else 'opf') elem = obj.create_metadata_element(self.name, is_dc=self.is_dc)
elem.text = unicode(val) obj.set_text(elem, unicode(val))
class OPF(object): class OPF(object):
MIMETYPE = 'application/oebps-package+xml' MIMETYPE = 'application/oebps-package+xml'
@ -403,16 +403,18 @@ class OPF(object):
'dc' : "http://purl.org/dc/elements/1.1/", 'dc' : "http://purl.org/dc/elements/1.1/",
'opf' : "http://www.idpf.org/2007/opf", 'opf' : "http://www.idpf.org/2007/opf",
} }
META = '{%s}meta' % NAMESPACES['opf']
xpn = NAMESPACES.copy() xpn = NAMESPACES.copy()
xpn.pop(None) xpn.pop(None)
xpn['re'] = 'http://exslt.org/regular-expressions' xpn['re'] = 'http://exslt.org/regular-expressions'
XPath = functools.partial(etree.XPath, namespaces=xpn) XPath = functools.partial(etree.XPath, namespaces=xpn)
CONTENT = XPath('self::*[re:match(name(), "meta$", "i")]/@content')
TEXT = XPath('string()') TEXT = XPath('string()')
metadata_path = XPath('descendant::*[re:match(name(), "metadata", "i")]') metadata_path = XPath('descendant::*[re:match(name(), "metadata", "i")]')
metadata_elem_path = XPath('descendant::*[re:match(name(), $name, "i")]') metadata_elem_path = XPath('descendant::*[re:match(name(), $name, "i") or (re:match(name(), "^meta$", "i") and re:match(@name, $name, "i"))]')
series_path = XPath('descendant::*[re:match(name(), "series$", "i")]') series_path = XPath('descendant::*[re:match(name(), "series$", "i") or (re:match(name(), "^meta$", "i") and re:match(@name, "series$", "i"))]')
authors_path = XPath('descendant::*[re:match(name(), "creator", "i") and (@role="aut" or @opf:role="aut" or (not(@role) and not(@opf:role)))]') authors_path = XPath('descendant::*[re:match(name(), "creator", "i") and (@role="aut" or @opf:role="aut" or (not(@role) and not(@opf:role)))]')
bkp_path = XPath('descendant::*[re:match(name(), "contributor", "i") and (@role="bkp" or @opf:role="bkp")]') bkp_path = XPath('descendant::*[re:match(name(), "contributor", "i") and (@role="bkp" or @opf:role="bkp")]')
tags_path = XPath('descendant::*[re:match(name(), "subject", "i")]') tags_path = XPath('descendant::*[re:match(name(), "subject", "i")]')
@ -497,7 +499,13 @@ class OPF(object):
def get_text(self, elem): def get_text(self, elem):
return u''.join(self.TEXT(elem)) return u''.join(self.CONTENT(elem) or self.TEXT(elem))
def set_text(self, elem, content):
if elem.tag == self.META:
elem.attib['content'] = content
else:
elem.text = content
def itermanifest(self): def itermanifest(self):
return self.manifest_path(self.root) return self.manifest_path(self.root)
@ -611,9 +619,9 @@ class OPF(object):
for elem in remove: for elem in remove:
self.metadata.remove(elem) self.metadata.remove(elem)
for author in val: for author in val:
elem = self.create_metadata_element('creator', ns='dc', attrib = {'{%s}role'%self.NAMESPACES['opf']: 'aut'}
attrib={'{%s}role'%self.NAMESPACES['opf']:'aut'}) elem = self.create_metadata_element('creator', attrib=attrib)
elem.text = author self.set_text(elem, author)
return property(fget=fget, fset=fset) return property(fget=fget, fset=fset)
@ -650,8 +658,8 @@ class OPF(object):
for tag in list(self.tags_path(self.metadata)): for tag in list(self.tags_path(self.metadata)):
self.metadata.remove(tag) self.metadata.remove(tag)
for tag in val: for tag in val:
elem = self.create_metadata_element('subject', ns='dc') elem = self.create_metadata_element('subject')
elem.text = unicode(tag) self.set_text(elem, unicode(tag))
return property(fget=fget, fset=fset) return property(fget=fget, fset=fset)
@ -660,14 +668,15 @@ class OPF(object):
def fget(self): def fget(self):
for match in self.isbn_path(self.metadata): for match in self.isbn_path(self.metadata):
return match.text if match.text else None return self.get_text(match) or None
def fset(self, val): def fset(self, val):
matches = self.isbn_path(self.metadata) matches = self.isbn_path(self.metadata)
if not matches: if not matches:
matches = [self.create_metadata_element('identifier', ns='dc', attrib = {'{%s}scheme'%self.NAMESPACES['opf']: 'ISBN'}
attrib={'{%s}scheme'%self.NAMESPACES['opf']:'ISBN'})] matches = [self.create_metadata_element('identifier',
matches[0].text = unicode(val) attrib=attrib)]
self.set_text(matches[0], unicode(val))
return property(fget=fget, fset=fset) return property(fget=fget, fset=fset)
@ -676,14 +685,15 @@ class OPF(object):
def fget(self): def fget(self):
for match in self.application_id_path(self.metadata): for match in self.application_id_path(self.metadata):
return match.text if match.text else None return self.get_text(match) or None
def fset(self, val): def fset(self, val):
matches = self.application_id_path(self.metadata) matches = self.application_id_path(self.metadata)
if not matches: if not matches:
matches = [self.create_metadata_element('identifier', ns='dc', attrib = {'{%s}scheme'%self.NAMESPACES['opf']: 'calibre'}
attrib={'{%s}scheme'%self.NAMESPACES['opf']:'calibre'})] matches = [self.create_metadata_element('identifier',
matches[0].text = unicode(val) attrib=attrib)]
self.set_text(matches[0], unicode(val))
return property(fget=fget, fset=fset) return property(fget=fget, fset=fset)
@ -693,13 +703,13 @@ class OPF(object):
def fget(self): def fget(self):
for match in self.series_path(self.metadata): for match in self.series_path(self.metadata):
return match.text if match.text else None return self.get_text(match) or None
def fset(self, val): def fset(self, val):
matches = self.series_path(self.metadata) matches = self.series_path(self.metadata)
if not matches: if not matches:
matches = [self.create_metadata_element('series')] matches = [self.create_metadata_element('series', is_dc=False)]
matches[0].text = unicode(val) self.set_text(matches[0], unicode(val))
return property(fget=fget, fset=fset) return property(fget=fget, fset=fset)
@ -710,14 +720,15 @@ class OPF(object):
def fget(self): def fget(self):
for match in self.bkp_path(self.metadata): for match in self.bkp_path(self.metadata):
return match.text if match.text else None return self.get_text(match) or None
def fset(self, val): def fset(self, val):
matches = self.bkp_path(self.metadata) matches = self.bkp_path(self.metadata)
if not matches: if not matches:
matches = [self.create_metadata_element('contributor', ns='dc', attrib = {'{%s}role'%self.NAMESPACES['opf']: 'bkp'}
attrib={'{%s}role'%self.NAMESPACES['opf']:'bkp'})] matches = [self.create_metadata_element('contributor',
matches[0].text = unicode(val) attrib=attrib)]
self.set_text(matches[0], unicode(val))
return property(fget=fget, fset=fset) return property(fget=fget, fset=fset)
@ -783,9 +794,15 @@ class OPF(object):
if matches: if matches:
return matches[-1] return matches[-1]
def create_metadata_element(self, name, attrib=None, ns='opf'): def create_metadata_element(self, name, attrib=None, is_dc=True):
elem = etree.SubElement(self.metadata, '{%s}%s'%(self.NAMESPACES[ns], name), if is_dc:
attrib=attrib, nsmap=self.NAMESPACES) name = '{%s}%s' % (self.NAMESPACES['dc'], name)
else:
attrib = attrib or {}
attrib['name'] = name
name = '{%s}%s' % (self.NAMESPACES['opf'], 'meta')
elem = etree.SubElement(self.metadata, name, attrib=attrib,
nsmap=self.NAMESPACES)
elem.tail = '\n' elem.tail = '\n'
return elem return elem

View File

@ -15,7 +15,8 @@
# # # #
# # # #
######################################################################### #########################################################################
import sys, os import sys, os, shutil
class Copy: class Copy:
"""Copy each changed file to a directory for debugging purposes""" """Copy each changed file to a directory for debugging purposes"""
__dir = "" __dir = ""
@ -64,25 +65,7 @@ class Copy:
of cp. Otherwise, use a safe python method. of cp. Otherwise, use a safe python method.
""" """
write_file = os.path.join(Copy.__dir,new_file) write_file = os.path.join(Copy.__dir,new_file)
platform = sys.platform shutil.copyfile(file, write_file)
if platform[:5] == 'linux':
command = 'cp %(file)s %(write_file)s' % vars()
os.system(command)
else:
read_obj = open(file,'r')
write_obj = open(write_file, 'w')
line = "dummy"
while line:
line = read_obj.read(1000)
write_obj.write(line )
read_obj.close()
write_obj.close()
def rename(self, source, dest): def rename(self, source, dest):
read_obj = open(source, 'r') shutil.copyfile(source, dest)
write_obj = open(dest, 'w')
line = 1
while line:
line = read_obj.readline()
write_obj.write(line)
read_obj.close()
write_obj.close()

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -39,15 +39,3 @@ def Algorithm(**args):
def KeyDerivation(**args): def KeyDerivation(**args):
return Element(qname = (MANIFESTNS,'key-derivation'), **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()

View File

@ -17,7 +17,7 @@
# #
# Contributor(s): # Contributor(s):
# #
TOOLSVERSION = u"ODFPY/0.8.1dev" TOOLSVERSION = u"ODFPY/0.8.2dev"
ANIMNS = u"urn:oasis:names:tc:opendocument:xmlns:animation:1.0" ANIMNS = u"urn:oasis:names:tc:opendocument:xmlns:animation:1.0"
DBNS = u"urn:oasis:names:tc:opendocument:xmlns:database:1.0" DBNS = u"urn:oasis:names:tc:opendocument:xmlns:database:1.0"
@ -49,7 +49,7 @@ TABLENS = u"urn:oasis:names:tc:opendocument:xmlns:table:1.0"
TEXTNS = u"urn:oasis:names:tc:opendocument:xmlns:text:1.0" TEXTNS = u"urn:oasis:names:tc:opendocument:xmlns:text:1.0"
XFORMSNS = u"http://www.w3.org/2002/xforms" XFORMSNS = u"http://www.w3.org/2002/xforms"
XLINKNS = u"http://www.w3.org/1999/xlink" XLINKNS = u"http://www.w3.org/1999/xlink"
XMLNS = "http://www.w3.org/XML/1998/namespace" XMLNS = u"http://www.w3.org/XML/1998/namespace"
nsdict = { nsdict = {

View File

@ -64,6 +64,12 @@ odmimetypes = {
'application/vnd.oasis.opendocument.text-web': '.oth', 'application/vnd.oasis.opendocument.text-web': '.oth',
} }
class OpaqueObject:
def __init__(self, filename, mediatype, content=None):
self.mediatype = mediatype
self.filename = filename
self.content = content
class OpenDocument: class OpenDocument:
""" A class to hold the content of an OpenDocument document """ A class to hold the content of an OpenDocument document
Use the xml method to write the XML Use the xml method to write the XML
@ -76,6 +82,7 @@ class OpenDocument:
def __init__(self, mimetype, add_generator=True): def __init__(self, mimetype, add_generator=True):
self.mimetype = mimetype self.mimetype = mimetype
self.childobjects = [] self.childobjects = []
self._extra = []
self.folder = "" # Always empty for toplevel documents self.folder = "" # Always empty for toplevel documents
self.topnode = Document(mimetype=self.mimetype) self.topnode = Document(mimetype=self.mimetype)
self.topnode.ownerDocument = self self.topnode.ownerDocument = self
@ -303,12 +310,15 @@ class OpenDocument:
else: else:
self.thumbnail = filecontent self.thumbnail = filecontent
def addObject(self, document): def addObject(self, document, objectname=None):
""" Add an object. The object must be an OpenDocument class """ 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 The return value will be the folder in the zipfile the object is stored in
""" """
self.childobjects.append(document) self.childobjects.append(document)
document.folder = "%s/Object %d" % (self.folder, len(self.childobjects)) if objectname is None:
document.folder = "%s/Object %d" % (self.folder, len(self.childobjects))
else:
document.folder = objectname
return ".%s" % document.folder return ".%s" % document.folder
def _savePictures(self, object, folder): def _savePictures(self, object, folder):
@ -348,7 +358,7 @@ class OpenDocument:
else: else:
if addsuffix: if addsuffix:
outputfile = outputfile + odmimetypes.get(self.mimetype,'.xxx') outputfile = outputfile + odmimetypes.get(self.mimetype,'.xxx')
outputfp = zipfile.ZipFile(outputfile,"w") outputfp = zipfile.ZipFile(outputfile, "w")
self._zipwrite(outputfp) self._zipwrite(outputfp)
outputfp.close() outputfp.close()
@ -382,6 +392,14 @@ class OpenDocument:
zi.external_attr = UNIXPERMS zi.external_attr = UNIXPERMS
self._z.writestr(zi, self.thumbnail) self._z.writestr(zi, self.thumbnail)
# Write any extra files
for op in self._extra:
self.manifest.addElement(manifest.FileEntry(fullpath=op.filename, mediatype=op.mediatype))
zi = zipfile.ZipInfo(op.filename.encode('utf-8'), self._now)
zi.compress_type = zipfile.ZIP_DEFLATED
zi.external_attr = UNIXPERMS
if op.content is not None:
self._z.writestr(zi, op.content)
# Write manifest # Write manifest
zi = zipfile.ZipInfo("META-INF/manifest.xml", self._now) zi = zipfile.ZipInfo("META-INF/manifest.xml", self._now)
zi.compress_type = zipfile.ZIP_DEFLATED zi.compress_type = zipfile.ZIP_DEFLATED
@ -528,15 +546,20 @@ def load(odffile):
parser.parse(inpsrc) parser.parse(inpsrc)
del doc._parsing del doc._parsing
except KeyError, v: pass except KeyError, v: pass
# Add the thumbnail here # FIXME: Add subobjects correctly here
# Add the images here
for mentry,mvalue in manifest.items(): for mentry,mvalue in manifest.items():
if mentry[:9] == "Pictures/" and len(mentry) > 9: if mentry[:9] == "Pictures/" and len(mentry) > 9:
doc.addPicture(mvalue['full-path'], mvalue['media-type'], z.read(mentry)) doc.addPicture(mvalue['full-path'], mvalue['media-type'], z.read(mentry))
elif mentry == "Thumbnails/thumbnail.png": elif mentry == "Thumbnails/thumbnail.png":
doc.addThumbnail(z.read(mentry)) doc.addThumbnail(z.read(mentry))
elif mentry in ('settings.xml', 'meta.xml', 'content.xml', 'styles.xml'):
pass
else: else:
pass # Add the SUN junk here to the struct somewhere if mvalue['full-path'][-1] == '/':
doc._extra.append(OpaqueObject(mvalue['full-path'], mvalue['media-type'], None))
else:
doc._extra.append(OpaqueObject(mvalue['full-path'], mvalue['media-type'], z.read(mentry)))
# Add the SUN junk here to the struct somewhere
# It is cached data, so it can be out-of-date # It is cached data, so it can be out-of-date
z.close() z.close()
b = doc.getElementsByType(Body) b = doc.getElementsByType(Body)

View File

@ -231,6 +231,19 @@ class ODFContentParser(xml.sax.saxutils.XMLGenerator):
self._callback_func = callback_func self._callback_func = callback_func
xml.sax.saxutils.XMLGenerator.__init__(self, out, encoding) xml.sax.saxutils.XMLGenerator.__init__(self, out, encoding)
def _qname(self, name):
"""Builds a qualified name from a (ns_url, localname) pair"""
if name[0]:
if name[0] == u'http://www.w3.org/XML/1998/namespace':
return u'xml' + ":" + name[1]
# The name is in a non-empty namespace
prefix = self._current_context[name[0]]
if prefix:
# If it is not the default namespace, prepend the prefix
return prefix + ":" + name[1]
# Return the unqualified name
return name[1]
def startElementNS(self, name, qname, attrs): def startElementNS(self, name, qname, attrs):
if name == (TEXTNS, u'user-field-decl'): if name == (TEXTNS, u'user-field-decl'):
field_name = attrs.get((TEXTNS, u'name')) field_name = attrs.get((TEXTNS, u'name'))