Allow reading more image formats as covers (All images will be converted to JPEG when stored). Fixes #1619993 [Request: Support for dragging TIFF files into Cover area](https://bugs.launchpad.net/calibre/+bug/1619993)

This commit is contained in:
Kovid Goyal 2016-09-04 08:29:20 +05:30
parent 72d735b41d
commit 5088a023c7
8 changed files with 32 additions and 22 deletions

View File

@ -750,9 +750,11 @@ else:
ans = ans[0] ans = ans[0]
return ans return ans
def choose_images(window, name, title, select_only_single_file=True, def choose_images(window, name, title, select_only_single_file=True, formats=None):
formats=('png', 'gif', 'jpg', 'jpeg', 'svg')):
mode = QFileDialog.ExistingFile if select_only_single_file else QFileDialog.ExistingFiles mode = QFileDialog.ExistingFile if select_only_single_file else QFileDialog.ExistingFiles
if formats is None:
from calibre.gui2.dnd import image_extensions
formats = image_extensions()
fd = FileDialog(title=title, name=name, fd = FileDialog(title=title, name=name,
filters=[(_('Images'), list(formats))], filters=[(_('Images'), list(formats))],
parent=window, add_all_files_filter=False, mode=mode, parent=window, add_all_files_filter=False, mode=mode,

View File

@ -17,7 +17,6 @@ from calibre.gui2 import (error_dialog, choose_files, choose_dir,
from calibre.gui2.dialogs.add_empty_book import AddEmptyBookDialog from calibre.gui2.dialogs.add_empty_book import AddEmptyBookDialog
from calibre.gui2.dialogs.confirm_delete import confirm from calibre.gui2.dialogs.confirm_delete import confirm
from calibre.gui2.dialogs.progress import ProgressDialog from calibre.gui2.dialogs.progress import ProgressDialog
from calibre.gui2.widgets import IMAGE_EXTENSIONS
from calibre.ebooks import BOOK_EXTENSIONS from calibre.ebooks import BOOK_EXTENSIONS
from calibre.utils.filenames import ascii_filename from calibre.utils.filenames import ascii_filename
from calibre.utils.icu import sort_key from calibre.utils.icu import sort_key
@ -359,11 +358,12 @@ class AddAction(InterfaceAction):
return return
cid = db.id(current_idx.row()) if cid is None else cid cid = db.id(current_idx.row()) if cid is None else cid
formats = [] formats = []
from calibre.gui2.dnd import image_extensions
for path in paths: for path in paths:
ext = os.path.splitext(path)[1].lower() ext = os.path.splitext(path)[1].lower()
if ext: if ext:
ext = ext[1:] ext = ext[1:]
if ext in IMAGE_EXTENSIONS: if ext in image_extensions():
pmap = QPixmap() pmap = QPixmap()
pmap.load(path) pmap.load(path)
if not pmap.isNull(): if not pmap.isNull():

View File

@ -17,7 +17,7 @@ from PyQt5.QtWebKitWidgets import QWebView
from calibre import fit_image from calibre import fit_image
from calibre.gui2.dnd import (dnd_has_image, dnd_get_image, dnd_get_files, from calibre.gui2.dnd import (dnd_has_image, dnd_get_image, dnd_get_files,
IMAGE_EXTENSIONS, dnd_has_extension) dnd_has_extension, image_extensions)
from calibre.ebooks import BOOK_EXTENSIONS from calibre.ebooks import BOOK_EXTENSIONS
from calibre.ebooks.metadata.book.base import (field_metadata, Metadata) from calibre.ebooks.metadata.book.base import (field_metadata, Metadata)
from calibre.ebooks.metadata.book.render import mi_to_html from calibre.ebooks.metadata.book.render import mi_to_html
@ -639,11 +639,10 @@ class BookDetails(QWidget): # {{{
open_fmt_with = pyqtSignal(int, object, object) open_fmt_with = pyqtSignal(int, object, object)
# Drag 'n drop {{{ # Drag 'n drop {{{
DROPABBLE_EXTENSIONS = IMAGE_EXTENSIONS+BOOK_EXTENSIONS
def dragEnterEvent(self, event): def dragEnterEvent(self, event):
md = event.mimeData() md = event.mimeData()
if dnd_has_extension(md, self.DROPABBLE_EXTENSIONS, allow_all_extensions=True) or \ if dnd_has_extension(md, image_extensions() + BOOK_EXTENSIONS, allow_all_extensions=True) or \
dnd_has_image(md): dnd_has_image(md):
event.acceptProposedAction() event.acceptProposedAction()

View File

@ -13,7 +13,7 @@ from threading import Thread
from Queue import Queue, Empty from Queue import Queue, Empty
from PyQt5.Qt import QPixmap, Qt, QDialog, QLabel, QVBoxLayout, \ from PyQt5.Qt import QPixmap, Qt, QDialog, QLabel, QVBoxLayout, \
QDialogButtonBox, QProgressBar, QTimer, QUrl QDialogButtonBox, QProgressBar, QTimer, QUrl, QImageReader
from calibre.constants import DEBUG, iswindows from calibre.constants import DEBUG, iswindows
from calibre.ptempfile import PersistentTemporaryFile from calibre.ptempfile import PersistentTemporaryFile
@ -21,6 +21,12 @@ from calibre import browser, as_unicode, prints
from calibre.gui2 import error_dialog from calibre.gui2 import error_dialog
from calibre.utils.imghdr import what from calibre.utils.imghdr import what
def image_extensions():
if not hasattr(image_extensions, 'ans'):
image_extensions.ans = [bytes(x).decode('utf-8') for x in QImageReader.supportedImageFormats()]
return image_extensions.ans
# This is present for compatibility with old plugins, do not use
IMAGE_EXTENSIONS = ['jpg', 'jpeg', 'gif', 'png', 'bmp'] IMAGE_EXTENSIONS = ['jpg', 'jpeg', 'gif', 'png', 'bmp']
class Worker(Thread): # {{{ class Worker(Thread): # {{{
@ -121,7 +127,7 @@ class DownloadDialog(QDialog): # {{{
def dnd_has_image(md): def dnd_has_image(md):
# Chromium puts image data into application/octet-stream # Chromium puts image data into application/octet-stream
return md.hasImage() or md.hasFormat('application/octet-stream') and what(None, bytes(md.data('application/octet-stream'))) in IMAGE_EXTENSIONS return md.hasImage() or md.hasFormat('application/octet-stream') and what(None, bytes(md.data('application/octet-stream'))) in image_extensions()
def data_as_string(f, md): def data_as_string(f, md):
raw = bytes(md.data(f)) raw = bytes(md.data(f))
@ -181,13 +187,12 @@ def dnd_has_extension(md, extensions, allow_all_extensions=False):
return bool(exts) return bool(exts)
return bool(exts.intersection(frozenset(extensions))) return bool(exts.intersection(frozenset(extensions)))
def dnd_get_image(md, image_exts=IMAGE_EXTENSIONS): def dnd_get_image(md, image_exts=None):
''' '''
Get the image in the QMimeData object md. Get the image in the QMimeData object md.
:return: None, None if no image is found :return: None, None if no image is found
QPixmap, None if an image is found, the pixmap is guaranteed not QPixmap, None if an image is found, the pixmap is guaranteed not null
null
url, filename if a URL that points to an image is found url, filename if a URL that points to an image is found
''' '''
if md.hasImage(): if md.hasImage():
@ -207,6 +212,9 @@ def dnd_get_image(md, image_exts=IMAGE_EXTENSIONS):
if not pmap.isNull(): if not pmap.isNull():
return pmap, None return pmap, None
if image_exts is None:
image_exts = image_extensions()
# No image, look for an URL pointing to an image # No image, look for an URL pointing to an image
urls = urls_from_md(md) urls = urls_from_md(md)
paths = [path_from_qurl(u) for u in urls] paths = [path_from_qurl(u) for u in urls]

View File

@ -1071,8 +1071,7 @@ class Cover(ImageView): # {{{
def select_cover(self, *args): def select_cover(self, *args):
files = choose_images( files = choose_images(
self, 'change cover dialog', _('Choose cover for ') + self.dialog.title.current_val, self, 'change cover dialog', _('Choose cover for ') + self.dialog.title.current_val)
formats=('png', 'gif', 'jpg', 'jpeg'))
if not files: if not files:
return return
_file = files[0] _file = files[0]

View File

@ -18,7 +18,7 @@ from PyQt5.Qt import (
from calibre import fit_image from calibre import fit_image
from calibre.gui2 import error_dialog, pixmap_to_data from calibre.gui2 import error_dialog, pixmap_to_data
from calibre.gui2.dnd import ( from calibre.gui2.dnd import (
IMAGE_EXTENSIONS, dnd_has_extension, dnd_has_image, dnd_get_image, DownloadDialog) image_extensions, dnd_has_extension, dnd_has_image, dnd_get_image, DownloadDialog)
from calibre.gui2.tweak_book import capitalize from calibre.gui2.tweak_book import capitalize
from calibre.utils.imghdr import identify from calibre.utils.imghdr import identify
from calibre.utils.img import ( from calibre.utils.img import (
@ -224,11 +224,10 @@ class Canvas(QWidget):
return self.current_image is not self.original_image return self.current_image is not self.original_image
# Drag 'n drop {{{ # Drag 'n drop {{{
DROPABBLE_EXTENSIONS = IMAGE_EXTENSIONS
def dragEnterEvent(self, event): def dragEnterEvent(self, event):
md = event.mimeData() md = event.mimeData()
if dnd_has_extension(md, self.DROPABBLE_EXTENSIONS) or dnd_has_image(md): if dnd_has_extension(md, image_extensions()) or dnd_has_image(md):
event.acceptProposedAction() event.acceptProposedAction()
def dropEvent(self, event): def dropEvent(self, event):

View File

@ -20,7 +20,7 @@ from calibre.ebooks import BOOK_EXTENSIONS
from calibre.utils.config import prefs, XMLConfig from calibre.utils.config import prefs, XMLConfig
from calibre.gui2.progress_indicator import ProgressIndicator as _ProgressIndicator from calibre.gui2.progress_indicator import ProgressIndicator as _ProgressIndicator
from calibre.gui2.dnd import (dnd_has_image, dnd_get_image, dnd_get_files, from calibre.gui2.dnd import (dnd_has_image, dnd_get_image, dnd_get_files,
IMAGE_EXTENSIONS, dnd_has_extension, DownloadDialog) image_extensions, dnd_has_extension, DownloadDialog)
from calibre.utils.localization import localize_user_manual_link from calibre.utils.localization import localize_user_manual_link
history = XMLConfig('history') history = XMLConfig('history')
@ -215,14 +215,15 @@ class ImageDropMixin(object): # {{{
Adds support for dropping images onto widgets and a context menu for Adds support for dropping images onto widgets and a context menu for
copy/pasting images. copy/pasting images.
''' '''
DROPABBLE_EXTENSIONS = IMAGE_EXTENSIONS DROPABBLE_EXTENSIONS = None
def __init__(self): def __init__(self):
self.setAcceptDrops(True) self.setAcceptDrops(True)
def dragEnterEvent(self, event): def dragEnterEvent(self, event):
md = event.mimeData() md = event.mimeData()
if dnd_has_extension(md, self.DROPABBLE_EXTENSIONS) or \ exts = self.DROPABBLE_EXTENSIONS or image_extensions()
if dnd_has_extension(md, exts) or \
dnd_has_image(md): dnd_has_image(md):
event.acceptProposedAction() event.acceptProposedAction()

View File

@ -225,8 +225,10 @@ def choose_files(window, name, title,
return ans return ans
return None return None
def choose_images(window, name, title, select_only_single_file=True, def choose_images(window, name, title, select_only_single_file=True, formats=None):
formats=('png', 'gif', 'jpg', 'jpeg', 'svg')): if formats is None:
from calibre.gui2.dnd import image_extensions
formats = image_extensions()
file_types = [(_('Images'), list(formats))] file_types = [(_('Images'), list(formats))]
return choose_files(window, name, title, select_only_single_file=select_only_single_file, filters=file_types) return choose_files(window, name, title, select_only_single_file=select_only_single_file, filters=file_types)