From ae1446a91dc93e6ee5935cf437940c331287febe Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 25 Jun 2010 22:51:41 -0600 Subject: [PATCH 1/8] Use correct 3:4 aspect ratio for default cover image --- resources/images/default_cover.svg | 3191 +++++++++++++++++++ src/calibre/gui2/__init__.py | 4 +- src/calibre/gui2/dialogs/metadata_single.py | 4 +- src/calibre/gui2/library/models.py | 10 +- src/calibre/linux.py | 9 +- 5 files changed, 3210 insertions(+), 8 deletions(-) create mode 100644 resources/images/default_cover.svg diff --git a/resources/images/default_cover.svg b/resources/images/default_cover.svg new file mode 100644 index 0000000000..3faf04f61d --- /dev/null +++ b/resources/images/default_cover.svg @@ -0,0 +1,3191 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/calibre/gui2/__init__.py b/src/calibre/gui2/__init__.py index 3d50b35ec4..f286402a37 100644 --- a/src/calibre/gui2/__init__.py +++ b/src/calibre/gui2/__init__.py @@ -248,7 +248,7 @@ def info_dialog(parent, title, msg, det_msg='', show=False): class Dispatcher(QObject): '''Convenience class to ensure that a function call always happens in the - thread the reciver was created in.''' + thread the receiver was created in.''' dispatch_signal = pyqtSignal(object, object) def __init__(self, func): @@ -507,7 +507,7 @@ def pixmap_to_data(pixmap, format='JPEG'): buf = QBuffer(ba) buf.open(QBuffer.WriteOnly) pixmap.save(buf, format) - return str(ba.data()) + return bytes(ba.data()) class ResizableDialog(QDialog): diff --git a/src/calibre/gui2/dialogs/metadata_single.py b/src/calibre/gui2/dialogs/metadata_single.py index 817b3a7197..1d62ad95ec 100644 --- a/src/calibre/gui2/dialogs/metadata_single.py +++ b/src/calibre/gui2/dialogs/metadata_single.py @@ -90,7 +90,7 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog): COVER_FETCH_TIMEOUT = 240 # seconds def do_reset_cover(self, *args): - pix = QPixmap(I('book.svg')) + pix = QPixmap(I('default_cover.svg')) self.cover.setPixmap(pix) self.cover_changed = True self.cover_data = None @@ -408,7 +408,7 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog): if cover: pm.loadFromData(cover) if pm.isNull(): - pm = QPixmap(I('book.svg')) + pm = QPixmap(I('default_cover.svg')) else: self.cover_data = cover self.cover.setPixmap(pm) diff --git a/src/calibre/gui2/library/models.py b/src/calibre/gui2/library/models.py index fcbcf043fc..508b3e591c 100644 --- a/src/calibre/gui2/library/models.py +++ b/src/calibre/gui2/library/models.py @@ -43,6 +43,14 @@ class FormatPath(unicode): ans.deleted_after_upload = False return ans +_default_image = None + +def default_image(): + global _default_image + if _default_image is None: + _default_image = QImage(I('default_cover.svg')) + return _default_image + class BooksModel(QAbstractTableModel): # {{{ about_to_be_sorted = pyqtSignal(object, name='aboutToBeSorted') @@ -71,7 +79,7 @@ class BooksModel(QAbstractTableModel): # {{{ self.book_on_device = None self.editable_cols = ['title', 'authors', 'rating', 'publisher', 'tags', 'series', 'timestamp', 'pubdate'] - self.default_image = QImage(I('book.svg')) + self.default_image = default_image() self.sorted_on = DEFAULT_SORT self.sort_history = [self.sorted_on] self.last_search = '' # The last search performed on this model diff --git a/src/calibre/linux.py b/src/calibre/linux.py index 51711b5b0f..e19df02258 100644 --- a/src/calibre/linux.py +++ b/src/calibre/linux.py @@ -539,17 +539,20 @@ MIME = '''\ ''' -def render_svg(image, dest): +def render_svg(image, dest, width=128, height=128): from PyQt4.QtGui import QPainter, QImage from PyQt4.QtSvg import QSvgRenderer - svg = QSvgRenderer(image.readAll()) + image = image.readAll() if hasattr(image, 'readAll') else image + svg = QSvgRenderer(image) painter = QPainter() - image = QImage(128,128,QImage.Format_ARGB32_Premultiplied) + image = QImage(width, height, QImage.Format_ARGB32) painter.begin(image) painter.setRenderHints(QPainter.Antialiasing|QPainter.TextAntialiasing|QPainter.SmoothPixmapTransform|QPainter.HighQualityAntialiasing) painter.setCompositionMode(QPainter.CompositionMode_SourceOver) svg.render(painter) painter.end() + if dest is None: + return image image.save(dest) def main(): From d96204698a2bb70b05268d3f75bc818f6dc33679 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 26 Jun 2010 08:51:31 -0600 Subject: [PATCH 2/8] Fix #5983 (Problem with localized versions with non-ascii characters in default paths) --- src/calibre/gui2/main.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/calibre/gui2/main.py b/src/calibre/gui2/main.py index bd24af1619..ca896fc014 100644 --- a/src/calibre/gui2/main.py +++ b/src/calibre/gui2/main.py @@ -56,6 +56,8 @@ def init_qt(args): def get_default_library_path(): fname = _('Calibre Library') + if iswindows: + fname = 'Calibre Library' if isinstance(fname, unicode): try: fname = fname.encode(filesystem_encoding) From 5c62b43d94548a4609fc1c3c1f3bd91a8819fb36 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 26 Jun 2010 10:09:27 -0600 Subject: [PATCH 3/8] Move iTunes driver to the bottom of the list so it does't interfere with device detection for people that have iphones and an ereader plugged in --- src/calibre/customize/builtins.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/customize/builtins.py b/src/calibre/customize/builtins.py index 9c13e0062e..5ca1b2f131 100644 --- a/src/calibre/customize/builtins.py +++ b/src/calibre/customize/builtins.py @@ -499,7 +499,6 @@ plugins += [ ] # Order here matters. The first matched device is the one used. plugins += [ - ITUNES, HANLINV3, HANLINV5, BLACKBERRY, @@ -551,6 +550,7 @@ plugins += [ FOLDER_DEVICE_FOR_CONFIG, AVANT, MENTOR, + ITUNES, ] plugins += [x for x in list(locals().values()) if isinstance(x, type) and \ x.__name__.endswith('MetadataReader')] From 3f14bedb00aabf1d60d293779d63b015c2fe3f83 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 26 Jun 2010 11:42:36 -0600 Subject: [PATCH 4/8] ISBNDb metadata plugin: Fix bug causing only first page of results to be fetched --- src/calibre/ebooks/metadata/fetch.py | 9 +++++---- src/calibre/ebooks/metadata/isbndb.py | 3 ++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/calibre/ebooks/metadata/fetch.py b/src/calibre/ebooks/metadata/fetch.py index 50a6726a0f..8b82d3c972 100644 --- a/src/calibre/ebooks/metadata/fetch.py +++ b/src/calibre/ebooks/metadata/fetch.py @@ -273,10 +273,11 @@ def filter_metadata_results(item): def do_cover_check(item): item.has_cover = False - try: - item.has_cover = check_for_cover(item.isbn) - except: - pass # Cover not found + if item.isbn: + try: + item.has_cover = check_for_cover(item.isbn) + except: + pass # Cover not found def check_for_covers(items): threads = [Thread(target=do_cover_check, args=(item,)) for item in items] diff --git a/src/calibre/ebooks/metadata/isbndb.py b/src/calibre/ebooks/metadata/isbndb.py index d9f376c83d..356cc3f1b1 100644 --- a/src/calibre/ebooks/metadata/isbndb.py +++ b/src/calibre/ebooks/metadata/isbndb.py @@ -34,7 +34,8 @@ def fetch_metadata(url, max=100, timeout=5.): errmsg = soup.find('errormessage').string raise ISBNDBError('Error fetching metadata: '+errmsg) total_results = int(book_list['total_results']) - np = '&page_number=%s&'%(page_number+1) + page_number += 1 + np = '&page_number=%s&'%page_number url = re.sub(r'\&page_number=\d+\&', np, url) books.extend(book_list.findAll('bookdata')) max -= 1 From 3544fe468cdbab52b4d1c96fd1f1e45e233e6a2e Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 26 Jun 2010 18:53:54 -0600 Subject: [PATCH 5/8] Fix #5980 (Reflection in Cover Browser not reflecting cropped bottom edge) --- src/calibre/gui2/pictureflow/pictureflow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/gui2/pictureflow/pictureflow.cpp b/src/calibre/gui2/pictureflow/pictureflow.cpp index a100f60e75..d9d8fefef4 100644 --- a/src/calibre/gui2/pictureflow/pictureflow.cpp +++ b/src/calibre/gui2/pictureflow/pictureflow.cpp @@ -579,7 +579,7 @@ void PictureFlowPrivate::resetSlides() static QImage prepareSurface(QImage img, int w, int h) { Qt::TransformationMode mode = Qt::SmoothTransformation; - img = img.scaled(w, h, Qt::KeepAspectRatioByExpanding, mode); + img = img.scaled(w, h, Qt::IgnoreAspectRatio, mode); // slightly larger, to accommodate for the reflection int hs = int(h * REFLECTION_FACTOR); From 04f26c6c47b531020298fb73fbf2a19944bfa745 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 26 Jun 2010 19:14:38 -0600 Subject: [PATCH 6/8] Implement #5988 (Keyboard Shortcut for "Download metadata and covers") --- src/calibre/gui2/init.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/calibre/gui2/init.py b/src/calibre/gui2/init.py index efbe32a04e..f918a5843c 100644 --- a/src/calibre/gui2/init.py +++ b/src/calibre/gui2/init.py @@ -56,7 +56,8 @@ class ToolbarMixin(object): # {{{ partial(self.edit_metadata, False, bulk=True)) md.addSeparator() md.addAction(_('Download metadata and covers'), - partial(self.download_metadata, False, covers=True)) + partial(self.download_metadata, False, covers=True), + Qt.ControlModifier+Qt.Key_D) md.addAction(_('Download only metadata'), partial(self.download_metadata, False, covers=False)) md.addAction(_('Download only covers'), From 5e25f706d2665fde382383507ba85703807b1621 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 26 Jun 2010 19:20:14 -0600 Subject: [PATCH 7/8] Implement #5990 (support for sweex mm300) --- src/calibre/customize/builtins.py | 3 ++- src/calibre/devices/misc.py | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/calibre/customize/builtins.py b/src/calibre/customize/builtins.py index 5ca1b2f131..2944035182 100644 --- a/src/calibre/customize/builtins.py +++ b/src/calibre/customize/builtins.py @@ -453,7 +453,7 @@ from calibre.devices.hanvon.driver import N516, EB511, ALEX, AZBOOKA, THEBOOK from calibre.devices.edge.driver import EDGE from calibre.devices.teclast.driver import TECLAST_K3, NEWSMY, IPAPYRUS from calibre.devices.sne.driver import SNE -from calibre.devices.misc import PALMPRE, AVANT +from calibre.devices.misc import PALMPRE, AVANT, SWEEX from calibre.devices.folder_device.driver import FOLDER_DEVICE_FOR_CONFIG from calibre.devices.kobo.driver import KOBO @@ -550,6 +550,7 @@ plugins += [ FOLDER_DEVICE_FOR_CONFIG, AVANT, MENTOR, + SWEEX, ITUNES, ] plugins += [x for x in list(locals().values()) if isinstance(x, type) and \ diff --git a/src/calibre/devices/misc.py b/src/calibre/devices/misc.py index 4310c51421..86fb36b40c 100644 --- a/src/calibre/devices/misc.py +++ b/src/calibre/devices/misc.py @@ -49,3 +49,23 @@ class AVANT(USBMS): EBOOK_DIR_MAIN = '' SUPPORTS_SUB_DIRS = True +class SWEEX(USBMS): + name = 'Sweex Device Interface' + gui_name = 'Sweex' + description = _('Communicate with the Sweex MM300') + author = 'Kovid Goyal' + supported_platforms = ['windows', 'osx', 'linux'] + + # Ordered list of supported formats + FORMATS = ['epub', 'prc', 'fb2', 'html', 'rtf', 'chm', 'pdf', 'txt'] + + VENDOR_ID = [0x0525] + PRODUCT_ID = [0xa4a5] + BCD = [0x0319] + + VENDOR_NAME = 'SWEEX' + WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = 'EBOOKREADER' + + EBOOK_DIR_MAIN = '' + SUPPORTS_SUB_DIRS = True + From ce736fa6ea7acc65bc13207eae98dd29560e4c9d Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 26 Jun 2010 19:48:36 -0600 Subject: [PATCH 8/8] Fix #5991 (Import of HTML With Embedded Stylesheet Broken In 0.7.5) --- src/calibre/ebooks/oeb/base.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/calibre/ebooks/oeb/base.py b/src/calibre/ebooks/oeb/base.py index f4a76808ae..200ace0bdc 100644 --- a/src/calibre/ebooks/oeb/base.py +++ b/src/calibre/ebooks/oeb/base.py @@ -7,7 +7,7 @@ __license__ = 'GPL v3' __copyright__ = '2008, Marshall T. Vandegrift ' __docformat__ = 'restructuredtext en' -import os, re, uuid, logging, functools +import os, re, uuid, logging from mimetypes import types_map from collections import defaultdict from itertools import count @@ -808,17 +808,17 @@ class Manifest(object): pat = re.compile(r'&(%s);'%('|'.join(user_entities.keys()))) data = pat.sub(lambda m:user_entities[m.group(1)], data) - fromstring = functools.partial(etree.fromstring, parser=RECOVER_PARSER) + parser = etree.XMLParser(no_network=True, huge_tree=True) # Try with more & more drastic measures to parse def first_pass(data): try: - data = fromstring(data) + data = etree.fromstring(data, parser=parser) except etree.XMLSyntaxError, err: self.oeb.log.exception('Initial parse failed:') repl = lambda m: ENTITYDEFS.get(m.group(1), m.group(0)) data = ENTITY_RE.sub(repl, data) try: - data = fromstring(data) + data = etree.fromstring(data, parser=parser) except etree.XMLSyntaxError, err: self.oeb.logger.warn('Parsing file %r as HTML' % self.href) if err.args and err.args[0].startswith('Excessive depth'): @@ -832,9 +832,9 @@ class Manifest(object): elem.text = elem.text.strip('-') data = etree.tostring(data, encoding=unicode) try: - data = fromstring(data) + data = etree.fromstring(data, parser=parser) except etree.XMLSyntaxError: - data = fromstring(data) + data = etree.fromstring(data, parser=RECOVER_PARSER) return data data = first_pass(data) @@ -866,12 +866,12 @@ class Manifest(object): data = etree.tostring(data, encoding=unicode) try: - data = fromstring(data) + data = etree.fromstring(data, parser=parser) except: data = data.replace(':=', '=').replace(':>', '>') data = data.replace('', '') try: - data = fromstring(data) + data = etree.fromstring(data, parser=parser) except etree.XMLSyntaxError: self.oeb.logger.warn('Stripping comments and meta tags from %s'% self.href) @@ -882,7 +882,7 @@ class Manifest(object): "", '') data = data.replace("", '') - data = fromstring(data) + data = etree.fromstring(data) elif namespace(data.tag) != XHTML_NS: # OEB_DOC_NS, but possibly others ns = namespace(data.tag)