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,6 +424,7 @@ def entity_to_unicode(match, exceptions=[], encoding='cp1252'):
if isosx:
fdir = os.path.expanduser('~/.fonts')
try:
if not os.path.exists(fdir):
os.makedirs(fdir)
if not os.path.exists(os.path.join(fdir, 'LiberationSans_Regular.ttf')):
@ -432,6 +433,9 @@ if isosx:
l = {}
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
from calibre.utils.config import migrate

View File

@ -898,7 +898,7 @@ def config(defaults=None, config_name='html',
metadata('title', ['-t', '--title'], default=None,
help=_('Set the title. Default is to autodetect.'))
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,
help=_('The subject(s) of this book, as a comma separated list.'))
metadata('publisher', ['--publisher'], default=None,
@ -994,7 +994,9 @@ def merge_metadata(htmlfile, opf, opts):
val = getattr(opts, attr, None)
if val is None or val == _('Unknown') or val == [_('Unknown')]:
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()]
setattr(mi, attr, val)

View File

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

View File

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

View File

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

View File

@ -15,7 +15,8 @@
# #
# #
#########################################################################
import sys, os
import sys, os, shutil
class Copy:
"""Copy each changed file to a directory for debugging purposes"""
__dir = ""
@ -64,25 +65,7 @@ class Copy:
of cp. Otherwise, use a safe python method.
"""
write_file = os.path.join(Copy.__dir,new_file)
platform = sys.platform
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()
shutil.copyfile(file, write_file)
def rename(self, source, dest):
read_obj = open(source, 'r')
write_obj = open(dest, 'w')
line = 1
while line:
line = read_obj.readline()
write_obj.write(line)
read_obj.close()
write_obj.close()
shutil.copyfile(source, dest)

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

@ -2060,6 +2060,8 @@ allowed_children = {
),
(TEXTNS,u'chapter') : (
),
(TEXTNS,u'character-count') : (
),
(TEXTNS,u'conditional-text') : (
),
(TEXTNS,u'creation-date') : (
@ -2294,6 +2296,8 @@ allowed_children = {
(TEXTNS,u'illustration-index-entry-template'),
(TEXTNS,u'index-title-template'),
),
(TEXTNS,u'image-count') : (
),
# allowed_children
(TEXTNS,u'index-body') : (
(DR3DNS,u'scene'),
@ -2661,6 +2665,8 @@ allowed_children = {
),
(TEXTNS,u'page') : (
),
(TEXTNS,u'page-count') : (
),
(TEXTNS,u'page-continuation') : (
),
(TEXTNS,u'page-number') : (
@ -2672,6 +2678,8 @@ allowed_children = {
),
(TEXTNS,u'page-variable-set') : (
),
(TEXTNS,u'paragraph-count') : (
),
(TEXTNS,u'placeholder') : (
),
(TEXTNS,u'print-date') : (
@ -2686,6 +2694,8 @@ allowed_children = {
),
(TEXTNS,u'reference-mark-start') : (
),
(TEXTNS,u'reference-ref') : (
),
(TEXTNS,u'ruby') : (
(TEXTNS,u'ruby-base'),
(TEXTNS,u'ruby-text'),
@ -3035,6 +3045,8 @@ allowed_children = {
),
(TEXTNS,u'tab') : (
),
(TEXTNS,u'table-count') : (
),
(TEXTNS,u'table-formula') : (
),
(TEXTNS,u'table-index') : (
@ -3132,6 +3144,8 @@ allowed_children = {
),
(TEXTNS,u'variable-set') : (
),
(TEXTNS,u'word-count') : (
),
}
struct_elements = ( # Unused?
@ -3182,6 +3196,7 @@ allows_text = (
(PRESENTATIONNS,u'footer-decl'),
(PRESENTATIONNS,u'header-decl'),
(SVGNS,u'desc'),
(SVGNS,u'title'),
(TEXTNS,u'a'),
(TEXTNS,u'author-initials'),
(TEXTNS,u'author-name'),
@ -3403,7 +3418,6 @@ required_attributes = {
(DRAWNS,u'glue-point'): (
(SVGNS,u'y'),
(SVGNS,u'x'),
(DRAWNS,u'align'),
(DRAWNS,u'id'),
),
(DRAWNS,u'gradient'): (
@ -4297,35 +4311,47 @@ allowed_attributes = {
(ANIMNS,u'value'),
),
(ANIMNS,u'seq'):(
(PRESENTATIONNS,u'node-type'),
(SMILNS,u'decelerate'),
(SMILNS,u'begin'),
(SMILNS,u'end'),
(PRESENTATIONNS,u'group-id'),
(SMILNS,u'accelerate'),
(SMILNS,u'repeatDur'),
(SMILNS,u'endsync'),
(SMILNS,u'restartDefault'),
(PRESENTATIONNS,u'preset-class'),
(SMILNS,u'fillDefault'),
(PRESENTATIONNS,u'preset-id'),
(SMILNS,u'autoReverse'),
(PRESENTATIONNS,u'preset-sub-type'),
(SMILNS,u'repeatCount'),
(SMILNS,u'dur'),
(SMILNS,u'fill'),
(ANIMNS,u'id'),
(SMILNS,u'restart'),
(PRESENTATIONNS,u'group-id'),
(PRESENTATIONNS,u'master-element'),
(PRESENTATIONNS,u'node-type'),
(PRESENTATIONNS,u'preset-class'),
(PRESENTATIONNS,u'preset-id'),
(PRESENTATIONNS,u'preset-sub-type'),
(SMILNS,u'accelerate'),
(SMILNS,u'autoReverse'),
(SMILNS,u'begin'),
(SMILNS,u'decelerate'),
(SMILNS,u'dur'),
(SMILNS,u'end'),
(SMILNS,u'endsync'),
(SMILNS,u'fill'),
(SMILNS,u'fillDefault'),
(SMILNS,u'repeatCount'),
(SMILNS,u'repeatDur'),
(SMILNS,u'restart'),
(SMILNS,u'restartDefault'),
),
(ANIMNS,u'set'):(
(ANIMNS,u'sub-item'),
(SMILNS,u'accelerate'),
(SMILNS,u'accumulate'),
(SMILNS,u'autoReverse'),
(SMILNS,u'additive'),
(SMILNS,u'attributeName'),
(SMILNS,u'to'),
(ANIMNS,u'sub-item'),
(SMILNS,u'targetElement'),
(SMILNS,u'accumulate'),
(SMILNS,u'begin'),
(SMILNS,u'decelerate'),
(SMILNS,u'dur'),
(SMILNS,u'end'),
(SMILNS,u'fill'),
(SMILNS,u'fillDefault'),
(SMILNS,u'repeatCount'),
(SMILNS,u'repeatDur'),
(SMILNS,u'restart'),
(SMILNS,u'restartDefault'),
(SMILNS,u'targetElement'),
(SMILNS,u'to'),
),
(ANIMNS,u'transitionFilter'):(
(SMILNS,u'direction'),
@ -5789,6 +5815,8 @@ allowed_attributes = {
(MANIFESTNS,'salt'),
(MANIFESTNS,'iteration-count'),
),
(MANIFESTNS,u'manifest'):(
),
# allowed_attributes
(METANS,u'auto-reload'):(
(METANS,u'delay'),
@ -6096,7 +6124,7 @@ allowed_attributes = {
(CHARTNS,u'gap-width'),
(CHARTNS,u'interpolation'),
(CHARTNS,u'interval-major'),
(CHARTNS,u'interval-minor'),
(CHARTNS,u'interval-minor-divisor'),
(CHARTNS,u'japanese-candle-stick'),
(CHARTNS,u'label-arrangement'),
(CHARTNS,u'lines'),
@ -6117,6 +6145,7 @@ allowed_attributes = {
(CHARTNS,u'spline-resolution'),
(CHARTNS,u'stacked'),
(CHARTNS,u'symbol-height'),
(CHARTNS,u'symbol-name'),
(CHARTNS,u'symbol-type'),
(CHARTNS,u'symbol-width'),
(CHARTNS,u'text-overlap'),
@ -6236,11 +6265,9 @@ allowed_attributes = {
),
(STYLENS,u'footer'):(
(STYLENS,u'display'),
(STYLENS,u'dynamic-spacing'),
),
(STYLENS,u'footer-left'):(
(STYLENS,u'display'),
(STYLENS,u'dynamic-spacing'),
),
(STYLENS,u'footer-style'):(
),
@ -6437,7 +6464,6 @@ allowed_attributes = {
),
(STYLENS,u'header'):(
(STYLENS,u'display'),
(STYLENS,u'dynamic-spacing'),
),
(STYLENS,u'header-footer-properties'): (
(FONS,u'background-color'),
@ -6468,7 +6494,6 @@ allowed_attributes = {
),
(STYLENS,u'header-left'):(
(STYLENS,u'display'),
(STYLENS,u'dynamic-spacing'),
),
(STYLENS,u'header-style'):(
),
@ -6480,6 +6505,7 @@ allowed_attributes = {
(STYLENS,u'font-name'),
(STYLENS,u'vertical-pos'),
(STYLENS,u'vertical-rel'),
(SVGNS,u'y'),
(TEXTNS,u'min-label-distance'),
(TEXTNS,u'min-label-width'),
(TEXTNS,u'space-before'),
@ -6534,12 +6560,10 @@ allowed_attributes = {
(STYLENS,u'layout-grid-print'),
(STYLENS,u'layout-grid-ruby-below'),
(STYLENS,u'layout-grid-ruby-height'),
(STYLENS,u'name'),
(STYLENS,u'num-format'),
(STYLENS,u'num-letter-sync'),
(STYLENS,u'num-prefix'),
(STYLENS,u'num-suffix'),
(STYLENS,u'page-usage'),
(STYLENS,u'paper-tray-name'),
(STYLENS,u'print'),
(STYLENS,u'print-orientation'),
@ -7156,7 +7180,6 @@ allowed_attributes = {
(TABLENS,u'cell-range-address'),
),
(TABLENS,u'null-date'):(
(TABLENS,u'date-value-type'),
(TABLENS,u'value-type'),
),
(TABLENS,u'odd-columns'):(

View File

@ -39,15 +39,3 @@ def 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()

View File

@ -17,7 +17,7 @@
#
# 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"
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"
XFORMSNS = u"http://www.w3.org/2002/xforms"
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 = {

View File

@ -64,6 +64,12 @@ odmimetypes = {
'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:
""" A class to hold the content of an OpenDocument document
Use the xml method to write the XML
@ -76,6 +82,7 @@ class OpenDocument:
def __init__(self, mimetype, add_generator=True):
self.mimetype = mimetype
self.childobjects = []
self._extra = []
self.folder = "" # Always empty for toplevel documents
self.topnode = Document(mimetype=self.mimetype)
self.topnode.ownerDocument = self
@ -303,12 +310,15 @@ class OpenDocument:
else:
self.thumbnail = filecontent
def addObject(self, document):
def addObject(self, document, objectname=None):
""" 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)
if objectname is None:
document.folder = "%s/Object %d" % (self.folder, len(self.childobjects))
else:
document.folder = objectname
return ".%s" % document.folder
def _savePictures(self, object, folder):
@ -382,6 +392,14 @@ class OpenDocument:
zi.external_attr = UNIXPERMS
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
zi = zipfile.ZipInfo("META-INF/manifest.xml", self._now)
zi.compress_type = zipfile.ZIP_DEFLATED
@ -528,15 +546,20 @@ def load(odffile):
parser.parse(inpsrc)
del doc._parsing
except KeyError, v: pass
# Add the thumbnail here
# Add the images here
# FIXME: Add subobjects correctly 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))
elif mentry in ('settings.xml', 'meta.xml', 'content.xml', 'styles.xml'):
pass
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
z.close()
b = doc.getElementsByType(Body)

View File

@ -231,6 +231,19 @@ class ODFContentParser(xml.sax.saxutils.XMLGenerator):
self._callback_func = callback_func
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):
if name == (TEXTNS, u'user-field-decl'):
field_name = attrs.get((TEXTNS, u'name'))