Implement marking of images as covers

This commit is contained in:
Kovid Goyal 2013-11-30 17:43:44 +05:30
parent c306458357
commit c1ae0a275f
4 changed files with 77 additions and 2 deletions

View File

@ -697,6 +697,10 @@ class Container(object): # {{{
mdata.remove(child) mdata.remove(child)
if len(mdata) > 0: if len(mdata) > 0:
mdata[-1].tail = '\n ' mdata[-1].tail = '\n '
# Ensure name comes before content, needed for Nooks
for meta in self.opf_xpath('//opf:meta[@name="cover"]'):
if 'content' in meta.attrib:
meta.set('content', meta.attrib.pop('content'))
def serialize_item(self, name): def serialize_item(self, name):
data = self.parsed(name) data = self.parsed(name)

View File

@ -38,6 +38,12 @@ def get_azw3_raster_cover_name(container):
if items: if items:
return container.href_to_name(items[0].get('href')) return container.href_to_name(items[0].get('href'))
def mark_as_cover_azw3(container, name):
href = container.name_to_href(name, container.opf_name)
for item in container.opf_xpath('//opf:guide/opf:reference[@href and contains(@type, "cover")]'):
item.set('href', href)
container.dirty(container.opf_name)
def get_raster_cover_name(container): def get_raster_cover_name(container):
if container.book_type == 'azw3': if container.book_type == 'azw3':
return get_azw3_raster_cover_name(container) return get_azw3_raster_cover_name(container)
@ -54,6 +60,17 @@ def set_cover(container, cover_path, report):
else: else:
set_epub_cover(container, cover_path, report) set_epub_cover(container, cover_path, report)
def mark_as_cover(container, name):
if name not in container.mime_map:
raise ValueError('Cannot mark %s as cover as it does not exist' % name)
mt = container.mime_map[name]
if not is_raster_image(mt):
raise ValueError('Cannot mark %s as the cover image as it is not a raster image' % name)
if container.book_type == 'azw3':
mark_as_cover_azw3(container, name)
else:
mark_as_cover_epub(container, name)
############################################################################### ###############################################################################
# The delightful EPUB cover processing # The delightful EPUB cover processing
@ -100,6 +117,37 @@ def find_cover_image(container, strict=False):
if largest_cover[0]: if largest_cover[0]:
return largest_cover[0] return largest_cover[0]
def mark_as_cover_epub(container, name):
mmap = {v:k for k, v in container.manifest_id_map.iteritems()}
if name not in mmap:
raise ValueError('Cannot mark %s as cover as it is not in manifest' % name)
mid = mmap[name]
# Remove all entries from the opf that identify a raster image as cover
for meta in container.opf_xpath('//opf:meta[@name="cover" and @content]'):
container.remove_from_xml(meta)
for ref in container.opf_xpath('//opf:guide/opf:reference[@href and @type]'):
if ref.get('type').lower() not in COVER_TYPES:
continue
name = container.href_to_name(ref.get('href'), container.opf_name)
mt = container.mime_map.get(name, None)
if is_raster_image(mt):
container.remove_from_xml(ref)
# Add reference to image in <metadata>
for metadata in container.opf_xpath('//opf:metadata'):
m = metadata.makeelement(OPF('meta'), name='cover', content=mid)
container.insert_into_xml(metadata, m)
# If no entry for titlepage exists in guide, insert one that points to this
# image
if not container.opf_xpath('//opf:guide/opf:reference[@type="cover"]'):
for guide in container.opf_xpath('//opf:guide'):
container.insert_into_xml(guide, guide.makeelement(
OPF('reference', type='cover', href=container.name_to_href(name, container.opf_name))))
container.dirty(container.opf_name)
def find_cover_page(container): def find_cover_page(container):
'Find a document marked as a cover in the OPF' 'Find a document marked as a cover in the OPF'
mm = container.mime_map mm = container.mime_map

View File

@ -19,6 +19,7 @@ from calibre.ptempfile import PersistentTemporaryDirectory
from calibre.ebooks.oeb.base import urlnormalize from calibre.ebooks.oeb.base import urlnormalize
from calibre.ebooks.oeb.polish.main import SUPPORTED, tweak_polish from calibre.ebooks.oeb.polish.main import SUPPORTED, tweak_polish
from calibre.ebooks.oeb.polish.container import get_container as _gc, clone_container, guess_type from calibre.ebooks.oeb.polish.container import get_container as _gc, clone_container, guess_type
from calibre.ebooks.oeb.polish.cover import mark_as_cover
from calibre.ebooks.oeb.polish.pretty import fix_all_html, pretty_all from calibre.ebooks.oeb.polish.pretty import fix_all_html, pretty_all
from calibre.ebooks.oeb.polish.replace import rename_files from calibre.ebooks.oeb.polish.replace import rename_files
from calibre.ebooks.oeb.polish.split import split, merge, AbortError from calibre.ebooks.oeb.polish.split import split, merge, AbortError
@ -57,6 +58,7 @@ class Boss(QObject):
fl.rename_requested.connect(self.rename_requested) fl.rename_requested.connect(self.rename_requested)
fl.edit_file.connect(self.edit_file_requested) fl.edit_file.connect(self.edit_file_requested)
fl.merge_requested.connect(self.merge_requested) fl.merge_requested.connect(self.merge_requested)
fl.mark_requested.connect(self.mark_requested)
self.gui.central.current_editor_changed.connect(self.apply_current_editor_state) self.gui.central.current_editor_changed.connect(self.apply_current_editor_state)
self.gui.central.close_requested.connect(self.editor_close_requested) self.gui.central.close_requested.connect(self.editor_close_requested)
self.gui.central.search_panel.search_triggered.connect(self.search) self.gui.central.search_panel.search_triggered.connect(self.search)
@ -70,6 +72,18 @@ class Boss(QObject):
for ed in editors.itervalues(): for ed in editors.itervalues():
ed.apply_settings() ed.apply_settings()
def mark_requested(self, name, action):
if not self.check_opf_dirtied():
return
c = current_container()
if action == 'cover':
mark_as_cover(current_container(), name)
if c.opf_name in editors:
editors[c.opf_name].replace_data(c.raw_data(c.opf_name))
self.gui.file_list.build(c)
self.set_modified()
def mkdtemp(self, prefix=''): def mkdtemp(self, prefix=''):
self.container_count += 1 self.container_count += 1
return tempfile.mkdtemp(prefix='%s%05d-' % (prefix, self.container_count), dir=self.tdir) return tempfile.mkdtemp(prefix='%s%05d-' % (prefix, self.container_count), dir=self.tdir)

View File

@ -18,7 +18,8 @@ from PyQt4.Qt import (
from calibre import human_readable, sanitize_file_name_unicode from calibre import human_readable, sanitize_file_name_unicode
from calibre.ebooks.oeb.base import OEB_STYLES, OEB_DOCS from calibre.ebooks.oeb.base import OEB_STYLES, OEB_DOCS
from calibre.ebooks.oeb.polish.container import guess_type, OEB_FONTS from calibre.ebooks.oeb.polish.container import guess_type, OEB_FONTS
from calibre.ebooks.oeb.polish.cover import get_cover_page_name, get_raster_cover_name from calibre.ebooks.oeb.polish.cover import (
get_cover_page_name, get_raster_cover_name, is_raster_image)
from calibre.gui2 import error_dialog, choose_files from calibre.gui2 import error_dialog, choose_files
from calibre.gui2.tweak_book import current_container, elided_text from calibre.gui2.tweak_book import current_container, elided_text
from calibre.gui2.tweak_book.editor import syntax_from_mime from calibre.gui2.tweak_book.editor import syntax_from_mime
@ -83,6 +84,7 @@ class FileList(QTreeWidget):
rename_requested = pyqtSignal(object, object) rename_requested = pyqtSignal(object, object)
edit_file = pyqtSignal(object, object, object) edit_file = pyqtSignal(object, object, object)
merge_requested = pyqtSignal(object, object, object) merge_requested = pyqtSignal(object, object, object)
mark_requested = pyqtSignal(object, object)
def __init__(self, parent=None): def __init__(self, parent=None):
QTreeWidget.__init__(self, parent) QTreeWidget.__init__(self, parent)
@ -306,7 +308,10 @@ class FileList(QTreeWidget):
ci = self.currentItem() ci = self.currentItem()
if ci is not None: if ci is not None:
cn = unicode(ci.data(0, NAME_ROLE).toString()) cn = unicode(ci.data(0, NAME_ROLE).toString())
mt = unicode(ci.data(0, MIME_ROLE).toString())
m.addAction(QIcon(I('modified.png')), _('&Rename %s') % (elided_text(self.font(), cn)), self.edit_current_item) m.addAction(QIcon(I('modified.png')), _('&Rename %s') % (elided_text(self.font(), cn)), self.edit_current_item)
if is_raster_image(mt):
m.addAction(QIcon(I('default_cover.png')), _('Mark %s as cover image') % elided_text(self.font(), cn), partial(self.mark_as_cover, cn))
selected_map = defaultdict(list) selected_map = defaultdict(list)
for item in sel: for item in sel:
@ -340,6 +345,9 @@ class FileList(QTreeWidget):
if self.currentItem() is not None: if self.currentItem() is not None:
self.editItem(self.currentItem()) self.editItem(self.currentItem())
def mark_as_cover(self, name):
self.mark_requested.emit(name, 'cover')
def keyPressEvent(self, ev): def keyPressEvent(self, ev):
if ev.key() in (Qt.Key_Delete, Qt.Key_Backspace): if ev.key() in (Qt.Key_Delete, Qt.Key_Backspace):
ev.accept() ev.accept()
@ -544,6 +552,7 @@ class FileListWidget(QWidget):
rename_requested = pyqtSignal(object, object) rename_requested = pyqtSignal(object, object)
edit_file = pyqtSignal(object, object, object) edit_file = pyqtSignal(object, object, object)
merge_requested = pyqtSignal(object, object, object) merge_requested = pyqtSignal(object, object, object)
mark_requested = pyqtSignal(object, object)
def __init__(self, parent=None): def __init__(self, parent=None):
QWidget.__init__(self, parent) QWidget.__init__(self, parent)
@ -551,7 +560,7 @@ class FileListWidget(QWidget):
self.file_list = FileList(self) self.file_list = FileList(self)
self.layout().addWidget(self.file_list) self.layout().addWidget(self.file_list)
self.layout().setContentsMargins(0, 0, 0, 0) self.layout().setContentsMargins(0, 0, 0, 0)
for x in ('delete_requested', 'reorder_spine', 'rename_requested', 'edit_file', 'merge_requested'): for x in ('delete_requested', 'reorder_spine', 'rename_requested', 'edit_file', 'merge_requested', 'mark_requested'):
getattr(self.file_list, x).connect(getattr(self, x)) getattr(self.file_list, x).connect(getattr(self, x))
for x in ('delete_done', 'select_name'): for x in ('delete_done', 'select_name'):
setattr(self, x, getattr(self.file_list, x)) setattr(self, x, getattr(self.file_list, x))