MOBI Output:Add option to use author sort to set the author field in generated MOBI files

This commit is contained in:
Kovid Goyal 2009-01-31 18:23:47 -08:00
commit cfce3b878e
4 changed files with 99 additions and 52 deletions

View File

@ -296,9 +296,11 @@ class Serializer(object):
class MobiWriter(object): class MobiWriter(object):
COLLAPSE_RE = re.compile(r'[ \t\r\n\v]+') COLLAPSE_RE = re.compile(r'[ \t\r\n\v]+')
def __init__(self, compression=None, imagemax=None): def __init__(self, compression=None, imagemax=None,
prefer_author_sort=False):
self._compression = compression or UNCOMPRESSED self._compression = compression or UNCOMPRESSED
self._imagemax = imagemax or OTHER_MAX_IMAGE_SIZE self._imagemax = imagemax or OTHER_MAX_IMAGE_SIZE
self._prefer_author_sort = prefer_author_sort
def dump(self, oeb, path): def dump(self, oeb, path):
if hasattr(path, 'write'): if hasattr(path, 'write'):
@ -457,12 +459,19 @@ class MobiWriter(object):
for term in oeb.metadata: for term in oeb.metadata:
if term not in EXTH_CODES: continue if term not in EXTH_CODES: continue
code = EXTH_CODES[term] code = EXTH_CODES[term]
for item in oeb.metadata[term]: items = oeb.metadata[term]
if term == 'creator':
if self._prefer_author_sort:
creators = [unicode(c.file_as or c) for c in items]
else:
creators = [unicode(c) for c in items]
items = ['; '.join(creators)]
for item in items:
data = self.COLLAPSE_RE.sub(' ', unicode(item)) data = self.COLLAPSE_RE.sub(' ', unicode(item))
if term == 'identifier': if term == 'identifier':
if data.lower().startswith('urn:isbn:'): if data.lower().startswith('urn:isbn:'):
data = data[9:] data = data[9:]
elif item.get('scheme', '').lower() == 'isbn': elif item.scheme.lower() == 'isbn':
pass pass
else: else:
continue continue
@ -535,6 +544,9 @@ def config(defaults=None):
help=_('Render HTML tables as blocks of text instead of actual ' help=_('Render HTML tables as blocks of text instead of actual '
'tables. This is neccessary if the HTML contains very large ' 'tables. This is neccessary if the HTML contains very large '
'or complex tables.')) 'or complex tables.'))
mobi('prefer_author_sort', ['--prefer-author-sort'], default=False,
help=_('When present, use the author sorting information for '
'generating the Mobipocket author metadata.'))
profiles = c.add_group('profiles', _('Device renderer profiles. ' profiles = c.add_group('profiles', _('Device renderer profiles. '
'Affects conversion of font sizes, image rescaling and rasterization ' 'Affects conversion of font sizes, image rescaling and rasterization '
'of tables. Valid profiles are: %s.') % ', '.join(_profiles)) 'of tables. Valid profiles are: %s.') % ', '.join(_profiles))
@ -594,7 +606,8 @@ def oeb2mobi(opts, inpath):
trimmer.transform(oeb, context) trimmer.transform(oeb, context)
mobimlizer = MobiMLizer(ignore_tables=opts.ignore_tables) mobimlizer = MobiMLizer(ignore_tables=opts.ignore_tables)
mobimlizer.transform(oeb, context) mobimlizer.transform(oeb, context)
writer = MobiWriter(compression=compression, imagemax=imagemax) writer = MobiWriter(compression=compression, imagemax=imagemax,
prefer_author_sort=opts.prefer_author_sort)
writer.dump(oeb, outpath) writer.dump(oeb, outpath)
run_plugins_on_postprocess(outpath, 'mobi') run_plugins_on_postprocess(outpath, 'mobi')
logger.info(_('Output written to ') + outpath) logger.info(_('Output written to ') + outpath)

View File

@ -220,12 +220,37 @@ class Metadata(object):
CALIBRE_TERMS = set(['series', 'series_index', 'rating']) CALIBRE_TERMS = set(['series', 'series_index', 'rating'])
OPF_ATTRS = {'role': OPF('role'), 'file-as': OPF('file-as'), OPF_ATTRS = {'role': OPF('role'), 'file-as': OPF('file-as'),
'scheme': OPF('scheme'), 'event': OPF('event'), 'scheme': OPF('scheme'), 'event': OPF('event'),
'type': XSI('type'), 'id': 'id'} 'type': XSI('type'), 'lang': XML('lang'), 'id': 'id'}
OPF1_NSMAP = {'dc': DC11_NS, 'oebpackage': OPF1_NS} OPF1_NSMAP = {'dc': DC11_NS, 'oebpackage': OPF1_NS}
OPF2_NSMAP = {'opf': OPF2_NS, 'dc': DC11_NS, 'dcterms': DCTERMS_NS, OPF2_NSMAP = {'opf': OPF2_NS, 'dc': DC11_NS, 'dcterms': DCTERMS_NS,
'xsi': XSI_NS, 'calibre': CALIBRE_NS} 'xsi': XSI_NS, 'calibre': CALIBRE_NS}
class Item(object): class Item(object):
class Attribute(object):
def __init__(self, attr, allowed=None):
if not callable(attr):
attr_, attr = attr, lambda term: attr_
self.attr = attr
self.allowed = allowed
def term_attr(self, obj):
term = obj.term
if namespace(term) != DC11_NS:
term = OPF('meta')
allowed = self.allowed
if allowed is not None and term not in allowed:
raise AttributeError(
'attribute %r not valid for metadata term %r' \
% (self.attr(term), barename(obj.term)))
return self.attr(term)
def __get__(self, obj, cls):
if obj is None: return None
return obj.attrib.get(self.term_attr(obj), '')
def __set__(self, obj, value):
obj.attrib[self.term_attr(obj)] = value
def __init__(self, term, value, attrib={}, nsmap={}, **kwargs): def __init__(self, term, value, attrib={}, nsmap={}, **kwargs):
self.attrib = attrib = dict(attrib) self.attrib = attrib = dict(attrib)
self.nsmap = nsmap = dict(nsmap) self.nsmap = nsmap = dict(nsmap)
@ -246,26 +271,27 @@ class Metadata(object):
if isprefixname(value): if isprefixname(value):
attrib[attr] = qname(value, nsmap) attrib[attr] = qname(value, nsmap)
nsattr = Metadata.OPF_ATTRS.get(attr, attr) nsattr = Metadata.OPF_ATTRS.get(attr, attr)
if nsattr == OPF('scheme') and namespace(term) != DC11_NS:
# The opf:meta element takes @scheme, not @opf:scheme
nsattr = 'scheme'
if attr != nsattr: if attr != nsattr:
attrib[nsattr] = attrib.pop(attr) attrib[nsattr] = attrib.pop(attr)
self.__setattr__ = self._setattr
def __getattr__(self, name): def scheme(term):
attr = name.replace('_', '-') if term == OPF('meta'):
if attr in Metadata.OPF_ATTRS: return 'scheme'
attr = Metadata.OPF_ATTRS[attr] return OPF('scheme')
return self.attrib.get(attr, None) scheme = Attribute(scheme, [DC('identifier'), OPF('meta')])
raise AttributeError( file_as = Attribute(OPF('file-as'), [DC('creator'), DC('contributor')])
'%r object has no attribute %r' \ role = Attribute(OPF('role'), [DC('creator'), DC('contributor')])
% (self.__class__.__name__, name)) event = Attribute(OPF('event'), [DC('date')])
id = Attribute('id')
def _setattr(self, name, value): type = Attribute(XSI('type'), [DC('date'), DC('format'), DC('type')])
attr = name.replace('_', '-') lang = Attribute(XML('lang'), [DC('contributor'), DC('coverage'),
if attr in Metadata.OPF_ATTRS: DC('creator'), DC('publisher'),
attr = Metadata.OPF_ATTRS[attr] DC('relation'), DC('rights'),
self.attrib[attr] = value DC('source'), DC('subject'),
return OPF('meta')])
super(Item, self).__setattr__(self, name, value)
def __getitem__(self, key): def __getitem__(self, key):
return self.attrib[key] return self.attrib[key]

View File

@ -62,6 +62,7 @@ class Config(ResizableDialog, Ui_Dialog):
self.toc_title_label.setVisible(False) self.toc_title_label.setVisible(False)
self.opt_rescale_images.setVisible(False) self.opt_rescale_images.setVisible(False)
self.opt_ignore_tables.setVisible(False) self.opt_ignore_tables.setVisible(False)
self.opt_prefer_author_sort.setVisible(False)
def initialize(self): def initialize(self):
self.__w = [] self.__w = []

View File

@ -105,6 +105,36 @@
<string>Book Cover</string> <string>Book Cover</string>
</property> </property>
<layout class="QGridLayout" name="_2" > <layout class="QGridLayout" name="_2" >
<item row="0" column="0" >
<layout class="QHBoxLayout" name="_3" >
<item>
<widget class="ImageView" name="cover" >
<property name="text" >
<string/>
</property>
<property name="pixmap" >
<pixmap resource="../images.qrc" >:/images/book.svg</pixmap>
</property>
<property name="scaledContents" >
<bool>true</bool>
</property>
<property name="alignment" >
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="0" >
<widget class="QCheckBox" name="opt_prefer_metadata_cover" >
<property name="text" >
<string>Use cover from &amp;source file</string>
</property>
<property name="checked" >
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0" > <item row="1" column="0" >
<layout class="QVBoxLayout" name="_4" > <layout class="QVBoxLayout" name="_4" >
<property name="spacing" > <property name="spacing" >
@ -156,36 +186,6 @@
</item> </item>
</layout> </layout>
</item> </item>
<item row="2" column="0" >
<widget class="QCheckBox" name="opt_prefer_metadata_cover" >
<property name="text" >
<string>Use cover from &amp;source file</string>
</property>
<property name="checked" >
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="0" >
<layout class="QHBoxLayout" name="_3" >
<item>
<widget class="ImageView" name="cover" >
<property name="text" >
<string/>
</property>
<property name="pixmap" >
<pixmap resource="../images.qrc" >:/images/book.svg</pixmap>
</property>
<property name="scaledContents" >
<bool>true</bool>
</property>
<property name="alignment" >
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
</layout>
</item>
</layout> </layout>
<zorder>opt_prefer_metadata_cover</zorder> <zorder>opt_prefer_metadata_cover</zorder>
<zorder></zorder> <zorder></zorder>
@ -486,6 +486,13 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="6" column="0" >
<widget class="QCheckBox" name="opt_prefer_author_sort" >
<property name="text" >
<string>&amp;Use author sort to set author field in output</string>
</property>
</widget>
</item>
</layout> </layout>
</item> </item>
<item> <item>