From 6f6db1b47743d3209d9de980e685fb2905ae119c Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 15 Dec 2010 18:08:59 -0700 Subject: [PATCH 01/15] Fix #7914 (Details for adding Samsung E65 ereader to Calibre) --- src/calibre/devices/sne/driver.py | 6 +++--- src/calibre/gui2/device.py | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/calibre/devices/sne/driver.py b/src/calibre/devices/sne/driver.py index 0ccac13245..bb8d34c59c 100644 --- a/src/calibre/devices/sne/driver.py +++ b/src/calibre/devices/sne/driver.py @@ -23,16 +23,16 @@ class SNE(USBMS): FORMATS = ['epub', 'pdf', 'txt'] VENDOR_ID = [0x04e8] - PRODUCT_ID = [0x2051, 0x2053] + PRODUCT_ID = [0x2051, 0x2053, 0x2054] BCD = [0x0323] VENDOR_NAME = 'SAMSUNG' - WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = 'SNE-60' + WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = ['SNE-60', 'E65'] MAIN_MEMORY_VOLUME_LABEL = 'SNE Main Memory' STORAGE_CARD_VOLUME_LABEL = 'SNE Storage Card' - EBOOK_DIR_MAIN = 'Books' + EBOOK_DIR_MAIN = EBOOK_DIR_CARD_A = 'Books' SUPPORTS_SUB_DIRS = True diff --git a/src/calibre/gui2/device.py b/src/calibre/gui2/device.py index 92b5932406..3b071aa024 100644 --- a/src/calibre/gui2/device.py +++ b/src/calibre/gui2/device.py @@ -593,7 +593,6 @@ class DeviceMenu(QMenu): # {{{ # }}} - class DeviceMixin(object): # {{{ def __init__(self): From 1140fb8f0ce3010bae051b32d716fce6ecd90e16 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 15 Dec 2010 18:11:34 -0700 Subject: [PATCH 02/15] Fix #7911 (Updated recipe for Radikal) --- resources/recipes/radikal_tr.recipe | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/resources/recipes/radikal_tr.recipe b/resources/recipes/radikal_tr.recipe index 2d71c238dd..18021f1bb4 100644 --- a/resources/recipes/radikal_tr.recipe +++ b/resources/recipes/radikal_tr.recipe @@ -13,14 +13,16 @@ class Radikal_tr(BasicNewsRecipe): description = 'News from Turkey' publisher = 'radikal' category = 'news, politics, Turkey' - oldest_article = 2 + oldest_article = 7 max_articles_per_feed = 150 no_stylesheets = True encoding = 'cp1254' use_embedded_content = False masthead_url = 'http://www.radikal.com.tr/D/i/1/V2/radikal_logo.jpg' language = 'tr' - extra_css = ' @font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)} .article_description,body{font-family: Arial,Verdana,Helvetica,sans1,sans-serif } ' + extra_css = """ @font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)} + .article_description,body{font-family: Arial,Verdana,Helvetica,sans1,sans-serif} + """ conversion_options = { 'comment' : description @@ -34,7 +36,13 @@ class Radikal_tr(BasicNewsRecipe): remove_tags_after = dict(attrs={'id':'haberDetayYazi'}) - feeds = [(u'Yazarlar', u'http://www.radikal.com.tr/d/rss/RssYazarlar.xml')] + feeds = [ + (u'Yazarlar' , u'http://www.radikal.com.tr/d/rss/RssYazarlar.xml') + ,(u'Turkiye' , u'http://www.radikal.com.tr/d/rss/Rss_97.xml' ) + ,(u'Politika' , u'http://www.radikal.com.tr/d/rss/Rss_98.xml' ) + ,(u'Dis Haberler', u'http://www.radikal.com.tr/d/rss/Rss_100.xml' ) + ,(u'Ekonomi' , u'http://www.radikal.com.tr/d/rss/Rss_101.xml' ) + ] def print_version(self, url): articleid = url.rpartition('ArticleID=')[2] From 5cd412a7d5d7120481a002e7d438ed72cabf5a37 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 15 Dec 2010 18:34:03 -0700 Subject: [PATCH 03/15] EPUB metadata: When rendering the first page as cover, render it as XHTML. This means that if the EPUB is not valid XHTML the cover wont be rendered correctly. Necessary for rendering of SVG elements in the page when it does not have an .xhtml extension. Fixes #7909 (Issue with HarperCollins EPUB Covers) --- src/calibre/ebooks/__init__.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/calibre/ebooks/__init__.py b/src/calibre/ebooks/__init__.py index 9bdf937dd1..b4619551b7 100644 --- a/src/calibre/ebooks/__init__.py +++ b/src/calibre/ebooks/__init__.py @@ -122,7 +122,11 @@ def render_html(path_to_html, width=590, height=750): renderer = HTMLRenderer(page, loop) page.connect(page, SIGNAL('loadFinished(bool)'), renderer, Qt.QueuedConnection) - page.mainFrame().load(QUrl.fromLocalFile(path_to_html)) + # Can't use load as if the extension of path is not xhtml + # then it won't render SVG correctly, so set mimetype + # explicitly + page.mainFrame().setContent(open(path_to_html, 'rb').read(), + 'application/xhtml+xml', QUrl.fromLocalFile(path_to_html)) loop.exec_() renderer.loop = renderer.page = None del page From a7e22ca8717ca60cc6f687231aab5df06fef5de7 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 15 Dec 2010 18:54:33 -0700 Subject: [PATCH 04/15] EPUB metadata: If parsing of cover file as XHTML fails, retry parsing as plain HTML --- src/calibre/ebooks/__init__.py | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/calibre/ebooks/__init__.py b/src/calibre/ebooks/__init__.py index b4619551b7..da4d1178eb 100644 --- a/src/calibre/ebooks/__init__.py +++ b/src/calibre/ebooks/__init__.py @@ -22,6 +22,9 @@ class UnknownFormatError(Exception): class DRMError(ValueError): pass +class ParserError(ValueError): + pass + BOOK_EXTENSIONS = ['lrf', 'rar', 'zip', 'rtf', 'lit', 'txt', 'htm', 'xhtm', 'html', 'xhtml', 'pdf', 'pdb', 'pdr', 'prc', 'mobi', 'azw', 'doc', 'epub', 'fb2', 'djvu', 'lrx', 'cbr', 'cbz', 'cbc', 'oebzip', @@ -39,6 +42,10 @@ class HTMLRenderer(object): try: if not ok: raise RuntimeError('Rendering of HTML failed.') + de = self.page.mainFrame().documentElement() + pe = de.findFirst('parsererror') + if not pe.isNull(): + raise ParserError(pe.toPlainText()) image = QImage(self.page.viewportSize(), QImage.Format_ARGB32) image.setDotsPerMeterX(96*(100/2.54)) image.setDotsPerMeterY(96*(100/2.54)) @@ -104,7 +111,7 @@ def render_html_svg_workaround(path_to_html, log, width=590, height=750): return data -def render_html(path_to_html, width=590, height=750): +def render_html(path_to_html, width=590, height=750, as_xhtml=True): from PyQt4.QtWebKit import QWebPage from PyQt4.Qt import QEventLoop, QPalette, Qt, SIGNAL, QUrl, QSize from calibre.gui2 import is_ok_to_use_qt @@ -122,15 +129,18 @@ def render_html(path_to_html, width=590, height=750): renderer = HTMLRenderer(page, loop) page.connect(page, SIGNAL('loadFinished(bool)'), renderer, Qt.QueuedConnection) - # Can't use load as if the extension of path is not xhtml - # then it won't render SVG correctly, so set mimetype - # explicitly - page.mainFrame().setContent(open(path_to_html, 'rb').read(), - 'application/xhtml+xml', QUrl.fromLocalFile(path_to_html)) + if as_xhtml: + page.mainFrame().setContent(open(path_to_html, 'rb').read(), + 'application/xhtml+xml', QUrl.fromLocalFile(path_to_html)) + else: + page.mainFrame().load(QUrl.fromLocalFile(path_to_html)) loop.exec_() renderer.loop = renderer.page = None del page del loop + if isinstance(renderer.exception, ParserError) and as_xhtml: + return render_html(path_to_html, width=width, height=height, + as_xhtml=False) return renderer def check_ebook_format(stream, current_guess): From 3f3abd41bb88b726522d60afe6b72df0cbe7de13 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 15 Dec 2010 22:39:04 -0700 Subject: [PATCH 05/15] E-book viewer: Add page flip animations. Their duration can be controlled via the viewer Preferences --- src/calibre/gui2/viewer/config.ui | 100 +++++++++++++++++++----- src/calibre/gui2/viewer/documentview.py | 38 ++++++++- src/calibre/gui2/viewer/flip.py | 94 ++++++++++++++++++++++ 3 files changed, 209 insertions(+), 23 deletions(-) create mode 100644 src/calibre/gui2/viewer/flip.py diff --git a/src/calibre/gui2/viewer/config.ui b/src/calibre/gui2/viewer/config.ui index 4066daada2..8f2650d814 100644 --- a/src/calibre/gui2/viewer/config.ui +++ b/src/calibre/gui2/viewer/config.ui @@ -7,14 +7,14 @@ 0 0 479 - 606 + 591 Configure Ebook viewer - + :/images/config.png:/images/config.png @@ -164,14 +164,14 @@ - + Remember last used &window size - + Remember the &current page when quitting @@ -201,21 +201,21 @@ - + H&yphenate (break line in the middle of large words) - + The default language to use for hyphenation rules. If the book does not specify a language, this will be used. - + Default &language for hyphenation: @@ -225,30 +225,66 @@ - + &Resize images larger than the viewer window (needs restart) + + + + Page flip &duration: + + + opt_page_flip_duration + + + + + + + disabled + + + secs + + + 1 + + + 0.100000000000000 + + + 3.000000000000000 + + + 0.100000000000000 + + + 0.500000000000000 + + + - - - - &User stylesheet - - - - - - - - + + + + Qt::Vertical + + + + 20 + 40 + + + + @@ -268,6 +304,29 @@ + + + User &Stylesheet + + + + + + <p>A CSS stylesheet that can be used to control the look and feel of books. For examples, click <a href="http://www.mobileread.com/forums/showthread.php?t=51500">here</a>. + + + true + + + true + + + + + + + + @@ -281,7 +340,6 @@ standard_font max_view_width opt_remember_window_size - css buttonBox diff --git a/src/calibre/gui2/viewer/documentview.py b/src/calibre/gui2/viewer/documentview.py index afaea41bc6..d033ae133b 100644 --- a/src/calibre/gui2/viewer/documentview.py +++ b/src/calibre/gui2/viewer/documentview.py @@ -18,6 +18,7 @@ from PyQt4.QtWebKit import QWebPage, QWebView, QWebSettings from calibre.utils.config import Config, StringConfig from calibre.utils.localization import get_language from calibre.gui2.viewer.config_ui import Ui_Dialog +from calibre.gui2.viewer.flip import SlideFlip from calibre.gui2.shortcuts import Shortcuts, ShortcutConfig from calibre.constants import iswindows from calibre import prints, guess_type @@ -52,6 +53,9 @@ def config(defaults=None): help=_('Default language for hyphenation rules')) c.add_opt('remember_current_page', default=True, help=_('Save the current position in the document, when quitting')) + c.add_opt('page_flip_duration', default=0.5, + help=_('The time, in seconds, for the page flip animation. Default' + ' is half a second.')) fonts = c.add_group('FONTS', _('Font options')) fonts('serif_family', default='Times New Roman' if iswindows else 'Liberation Serif', @@ -75,6 +79,7 @@ class ConfigDialog(QDialog, Ui_Dialog): opts = config().parse() self.opt_remember_window_size.setChecked(opts.remember_window_size) self.opt_remember_current_page.setChecked(opts.remember_current_page) + self.opt_page_flip_duration.setValue(opts.page_flip_duration) self.serif_family.setCurrentFont(QFont(opts.serif_family)) self.sans_family.setCurrentFont(QFont(opts.sans_family)) self.mono_family.setCurrentFont(QFont(opts.mono_family)) @@ -122,6 +127,7 @@ class ConfigDialog(QDialog, Ui_Dialog): c.set('max_view_width', int(self.max_view_width.value())) c.set('hyphenate', self.hyphenate.isChecked()) c.set('remember_current_page', self.opt_remember_current_page.isChecked()) + c.set('page_flip_duration', self.opt_page_flip_duration.value()) idx = self.hyphenate_default_lang.currentIndex() c.set('hyphenate_default_lang', str(self.hyphenate_default_lang.itemData(idx).toString())) @@ -197,6 +203,8 @@ class Document(QWebPage): self.hyphenate = opts.hyphenate self.hyphenate_default_lang = opts.hyphenate_default_lang self.do_fit_images = opts.fit_images + self.page_flip_duration = opts.page_flip_duration + self.enable_page_flip = self.page_flip_duration > 0.1 def fit_images(self): if self.do_fit_images: @@ -453,6 +461,7 @@ class DocumentView(QWebView): def __init__(self, *args): QWebView.__init__(self, *args) + self.flipper = SlideFlip(self) self.debug_javascript = False self.shortcuts = Shortcuts(SHORTCUTS, 'shortcuts/viewer') self.self_closing_pat = re.compile(r'<([a-z1-6]+)\s+([^>]+)/>', @@ -708,12 +717,17 @@ class DocumentView(QWebView): return False return True - def find_next_blank_line(self, overlap): + def current_page_image(self, overlap=-1): + if overlap < 0: + overlap = self.height() img = QImage(self.width(), overlap, QImage.Format_ARGB32) painter = QPainter(img) - # Render a region of width x overlap pixels atthe bottom of the current viewport self.document.mainFrame().render(painter, QRegion(0, 0, self.width(), overlap)) painter.end() + return img + + def find_next_blank_line(self, overlap): + img = self.current_page_image(overlap) for i in range(overlap-1, -1, -1): if self.test_line(img, i): self.scroll_by(y=i, notify=False) @@ -721,6 +735,10 @@ class DocumentView(QWebView): self.scroll_by(y=overlap) def previous_page(self): + if self.flipper.running: + return + epf = self.document.enable_page_flip + delta_y = self.document.window_height - 25 if self.document.at_top: if self.manager is not None: @@ -732,11 +750,20 @@ class DocumentView(QWebView): if upper_limit < 0: upper_limit = 0 if upper_limit < opos: + if epf: + self.flipper.initialize(self.current_page_image()) self.document.scroll_to(self.document.xpos, upper_limit) + if epf: + self.flipper(self.current_page_image(), forwards=False, + duration=self.document.page_flip_duration) if self.manager is not None: self.manager.scrolled(self.scroll_fraction) def next_page(self): + if self.flipper.running: + return + epf = self.document.enable_page_flip + window_height = self.document.window_height document_height = self.document.height ddelta = document_height - window_height @@ -770,6 +797,10 @@ class DocumentView(QWebView): return #print 'Setting padding to:', lower_limit - max_y self.document.set_bottom_padding(lower_limit - max_y) + before_img = None + if epf: + before_img = self.current_page_image() + self.flipper.initialize(before_img) #print 'Document height:', self.document.height max_y = self.document.height - window_height lower_limit = min(max_y, lower_limit) @@ -780,6 +811,9 @@ class DocumentView(QWebView): #print 'After scroll pos:', self.document.ypos self.find_next_blank_line(window_height - actually_scrolled) #print 'After blank line pos:', self.document.ypos + if epf: + self.flipper(self.current_page_image(), + duration=self.document.page_flip_duration) if self.manager is not None: self.manager.scrolled(self.scroll_fraction) #print 'After all:', self.document.ypos diff --git a/src/calibre/gui2/viewer/flip.py b/src/calibre/gui2/viewer/flip.py new file mode 100644 index 0000000000..d12d3f56fe --- /dev/null +++ b/src/calibre/gui2/viewer/flip.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python +# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai + +__license__ = 'GPL v3' +__copyright__ = '2010, Kovid Goyal ' +__docformat__ = 'restructuredtext en' + +from PyQt4.Qt import QWidget, QPainter, QPropertyAnimation, QEasingCurve, \ + QRect, QPixmap, Qt, pyqtProperty + +class SlideFlip(QWidget): + + def __init__(self, parent): + QWidget.__init__(self, parent) + + self.setGeometry(0, 0, 1, 1) + self._current_width = 0 + self.before_image = self.after_image = None + self.animation = QPropertyAnimation(self, 'current_width', self) + self.setVisible(False) + self.animation.valueChanged.connect(self.update) + self.animation.finished.connect(self.finished) + self.flip_forwards = True + self.setAttribute(Qt.WA_OpaquePaintEvent) + + @property + def running(self): + return self.animation.state() == self.animation.Running + + def initialize(self, image): + self.before_image = QPixmap.fromImage(image) + self.after_image = None + self.setGeometry(0, 0, image.width(), image.height()) + self.setVisible(True) + + def __call__(self, image, duration=0.5, forwards=True): + if self.running: + return + self.flip_forwards = forwards + self.after_image = QPixmap.fromImage(image) + + if self.flip_forwards: + self.animation.setStartValue(image.width()) + self.animation.setEndValue(0) + t = self.before_image + self.before_image = self.after_image + self.after_image = t + self.animation.setEasingCurve(QEasingCurve(QEasingCurve.InExpo)) + else: + self.animation.setStartValue(0) + self.animation.setEndValue(image.width()) + self.animation.setEasingCurve(QEasingCurve(QEasingCurve.OutExpo)) + + self.animation.setDuration(duration * 1000) + self.animation.start() + + def finished(self): + self.setVisible(False) + self.before_image = self.after_image = None + + def paintEvent(self, ev): + if self.before_image is None: + return + canvas_size = self.rect() + p = QPainter(self) + p.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform) + + p.drawPixmap(canvas_size, self.before_image, + self.before_image.rect()) + if self.after_image is not None: + width = self._current_width + iw = self.after_image.width() + sh = min(self.after_image.height(), canvas_size.height()) + + if self.flip_forwards: + source = QRect(max(0, iw - width), 0, width, sh) + else: + source = QRect(0, 0, width, sh) + + target = QRect(source) + target.moveLeft(0) + p.drawPixmap(target, self.after_image, source) + + p.end() + + def set_current_width(self, val): + self._current_width = val + + current_width = pyqtProperty('int', + fget=lambda self: self._current_width, + fset=set_current_width + ) + + From 6d5f1fac3b8a69f23a680f3f083cbe687c009acd Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 15 Dec 2010 22:58:48 -0700 Subject: [PATCH 06/15] E-book viewer: Disable page flip animation when a key is help down --- src/calibre/gui2/viewer/documentview.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/calibre/gui2/viewer/documentview.py b/src/calibre/gui2/viewer/documentview.py index d033ae133b..8ccc0b0f77 100644 --- a/src/calibre/gui2/viewer/documentview.py +++ b/src/calibre/gui2/viewer/documentview.py @@ -462,6 +462,7 @@ class DocumentView(QWebView): def __init__(self, *args): QWebView.__init__(self, *args) self.flipper = SlideFlip(self) + self.is_auto_repeat_event = False self.debug_javascript = False self.shortcuts = Shortcuts(SHORTCUTS, 'shortcuts/viewer') self.self_closing_pat = re.compile(r'<([a-z1-6]+)\s+([^>]+)/>', @@ -735,9 +736,9 @@ class DocumentView(QWebView): self.scroll_by(y=overlap) def previous_page(self): - if self.flipper.running: + if self.flipper.running and not self.is_auto_repeat_event: return - epf = self.document.enable_page_flip + epf = self.document.enable_page_flip and not self.is_auto_repeat_event delta_y = self.document.window_height - 25 if self.document.at_top: @@ -760,9 +761,9 @@ class DocumentView(QWebView): self.manager.scrolled(self.scroll_fraction) def next_page(self): - if self.flipper.running: + if self.flipper.running and not self.is_auto_repeat_event: return - epf = self.document.enable_page_flip + epf = self.document.enable_page_flip and not self.is_auto_repeat_event window_height = self.document.window_height document_height = self.document.height @@ -896,7 +897,11 @@ class DocumentView(QWebView): key = self.shortcuts.get_match(event) func = self.goto_location_actions.get(key, None) if func is not None: - func() + self.is_auto_repeat_event = event.isAutoRepeat() + try: + func() + finally: + self.is_auto_repeat_event = False elif key == 'Down': self.scroll_by(y=15) elif key == 'Up': From fc774a15eefd8c7448d5a3b18f5d25d9a0aca4a6 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 15 Dec 2010 23:23:21 -0700 Subject: [PATCH 07/15] E-book viewer: Add option to have the mouse wheel flip pages --- src/calibre/gui2/viewer/config.ui | 7 +++++++ src/calibre/gui2/viewer/documentview.py | 14 ++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/src/calibre/gui2/viewer/config.ui b/src/calibre/gui2/viewer/config.ui index 8f2650d814..32188bdf7a 100644 --- a/src/calibre/gui2/viewer/config.ui +++ b/src/calibre/gui2/viewer/config.ui @@ -267,6 +267,13 @@ + + + + Mouse &wheel flips pages + + + diff --git a/src/calibre/gui2/viewer/documentview.py b/src/calibre/gui2/viewer/documentview.py index 8ccc0b0f77..4ce9b9818e 100644 --- a/src/calibre/gui2/viewer/documentview.py +++ b/src/calibre/gui2/viewer/documentview.py @@ -53,6 +53,8 @@ def config(defaults=None): help=_('Default language for hyphenation rules')) c.add_opt('remember_current_page', default=True, help=_('Save the current position in the document, when quitting')) + c.add_opt('wheel_flips_pages', default=False, + help=_('Have the mouse wheel turn pages')) c.add_opt('page_flip_duration', default=0.5, help=_('The time, in seconds, for the page flip animation. Default' ' is half a second.')) @@ -79,6 +81,7 @@ class ConfigDialog(QDialog, Ui_Dialog): opts = config().parse() self.opt_remember_window_size.setChecked(opts.remember_window_size) self.opt_remember_current_page.setChecked(opts.remember_current_page) + self.opt_wheel_flips_pages.setChecked(opts.wheel_flips_pages) self.opt_page_flip_duration.setValue(opts.page_flip_duration) self.serif_family.setCurrentFont(QFont(opts.serif_family)) self.sans_family.setCurrentFont(QFont(opts.sans_family)) @@ -127,6 +130,7 @@ class ConfigDialog(QDialog, Ui_Dialog): c.set('max_view_width', int(self.max_view_width.value())) c.set('hyphenate', self.hyphenate.isChecked()) c.set('remember_current_page', self.opt_remember_current_page.isChecked()) + c.set('wheel_flips_pages', self.opt_wheel_flips_pages.isChecked()) c.set('page_flip_duration', self.opt_page_flip_duration.value()) idx = self.hyphenate_default_lang.currentIndex() c.set('hyphenate_default_lang', @@ -205,6 +209,7 @@ class Document(QWebPage): self.do_fit_images = opts.fit_images self.page_flip_duration = opts.page_flip_duration self.enable_page_flip = self.page_flip_duration > 0.1 + self.wheel_flips_pages = opts.wheel_flips_pages def fit_images(self): if self.do_fit_images: @@ -868,6 +873,10 @@ class DocumentView(QWebView): def wheelEvent(self, event): if event.delta() < -14: + if self.document.wheel_flips_pages: + self.next_page() + event.accept() + return if self.document.at_bottom: self.scroll_by(y=15) # at_bottom can lie on windows if self.manager is not None: @@ -875,6 +884,11 @@ class DocumentView(QWebView): event.accept() return elif event.delta() > 14: + if self.document.wheel_flips_pages: + self.previous_page() + event.accept() + return + if self.document.at_top: if self.manager is not None: self.manager.previous_document() From 4b7705fbbe9d91f5a1cc22197a184a11fe3e89aa Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 15 Dec 2010 23:28:21 -0700 Subject: [PATCH 08/15] ... --- src/calibre/gui2/viewer/config.ui | 252 ++++++++++++++---------------- 1 file changed, 118 insertions(+), 134 deletions(-) diff --git a/src/calibre/gui2/viewer/config.ui b/src/calibre/gui2/viewer/config.ui index 32188bdf7a..6e37170154 100644 --- a/src/calibre/gui2/viewer/config.ui +++ b/src/calibre/gui2/viewer/config.ui @@ -85,11 +85,7 @@ - - - - - + &Default font size: @@ -99,7 +95,7 @@ - + px @@ -112,7 +108,7 @@ - + Monospace &font size: @@ -122,7 +118,7 @@ - + px @@ -135,7 +131,7 @@ - + S&tandard font: @@ -145,7 +141,7 @@ - + @@ -164,133 +160,124 @@ - - - - Remember last used &window size - - - - - - - Remember the &current page when quitting - - - - - - - px - - - 100 - - - 10000 - - - - - - - Maximum &view width: - - - max_view_width - - - - - - - H&yphenate (break line in the middle of large words) - - - - - - - The default language to use for hyphenation rules. If the book does not specify a language, this will be used. - - - - - - - Default &language for hyphenation: - - - hyphenate_default_lang - - - - - - - &Resize images larger than the viewer window (needs restart) - - - - - - - Page flip &duration: - - - opt_page_flip_duration - - - - - - - disabled - - - secs - - - 1 - - - 0.100000000000000 - - - 3.000000000000000 - - - 0.100000000000000 - - - 0.500000000000000 - - - - - - - Mouse &wheel flips pages - - - - - - Qt::Vertical - - - - 20 - 40 - - - + + + + + Remember last used &window size + + + + + + + Remember the &current page when quitting + + + + + + + H&yphenate (break line in the middle of large words) + + + + + + + The default language to use for hyphenation rules. If the book does not specify a language, this will be used. + + + + + + + Default &language for hyphenation: + + + hyphenate_default_lang + + + + + + + &Resize images larger than the viewer window (needs restart) + + + + + + + Page flip &duration: + + + opt_page_flip_duration + + + + + + + disabled + + + secs + + + 1 + + + 0.100000000000000 + + + 3.000000000000000 + + + 0.100000000000000 + + + 0.500000000000000 + + + + + + + Mouse &wheel flips pages + + + + + + + px + + + 100 + + + 10000 + + + + + + + Maximum &view width: + + + max_view_width + + + + @@ -342,9 +329,6 @@ serif_family sans_family mono_family - default_font_size - mono_font_size - standard_font max_view_width opt_remember_window_size buttonBox From 8e787097ac5766e0f3f4905b709cbcd8a5352410 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 16 Dec 2010 08:52:31 -0700 Subject: [PATCH 09/15] Fix #7919 (Conversion error) --- src/calibre/ebooks/oeb/stylizer.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/calibre/ebooks/oeb/stylizer.py b/src/calibre/ebooks/oeb/stylizer.py index 616cd3b800..40b82514c1 100644 --- a/src/calibre/ebooks/oeb/stylizer.py +++ b/src/calibre/ebooks/oeb/stylizer.py @@ -205,7 +205,10 @@ class Stylizer(object): NameError, # thrown on OS X instead of SelectorSyntaxError SelectorSyntaxError): continue - matches = selector(tree) + try: + matches = selector(tree) + except etree.XPathEvalError: + continue if not matches: ntext = capital_sel_pat.sub(lambda m: m.group().lower(), text) From cf190680027436ea3e85be42f3619d20304d4d0a Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 16 Dec 2010 09:07:29 -0700 Subject: [PATCH 10/15] Driver for PocketBook 701 --- src/calibre/customize/builtins.py | 7 +++---- src/calibre/devices/eb600/driver.py | 20 ++++++++++++++++++++ 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/calibre/customize/builtins.py b/src/calibre/customize/builtins.py index 5f3aab142e..793c1fa0de 100644 --- a/src/calibre/customize/builtins.py +++ b/src/calibre/customize/builtins.py @@ -457,7 +457,8 @@ from calibre.devices.blackberry.driver import BLACKBERRY from calibre.devices.cybook.driver import CYBOOK, ORIZON from calibre.devices.eb600.driver import EB600, COOL_ER, SHINEBOOK, \ POCKETBOOK360, GER2, ITALICA, ECLICTO, DBOOK, INVESBOOK, \ - BOOQ, ELONEX, POCKETBOOK301, MENTOR, POCKETBOOK602 + BOOQ, ELONEX, POCKETBOOK301, MENTOR, POCKETBOOK602, \ + POCKETBOOK701 from calibre.devices.iliad.driver import ILIAD from calibre.devices.irexdr.driver import IREXDR1000, IREXDR800 from calibre.devices.jetbook.driver import JETBOOK, MIBUK, JETBOOK_MINI @@ -545,9 +546,7 @@ plugins += [ JETBOOK_MINI, MIBUK, SHINEBOOK, - POCKETBOOK360, - POCKETBOOK301, - POCKETBOOK602, + POCKETBOOK360, POCKETBOOK301, POCKETBOOK602, POCKETBOOK701, KINDLE, KINDLE2, KINDLE_DX, diff --git a/src/calibre/devices/eb600/driver.py b/src/calibre/devices/eb600/driver.py index 54d73d9c1d..bc8b87533c 100644 --- a/src/calibre/devices/eb600/driver.py +++ b/src/calibre/devices/eb600/driver.py @@ -246,3 +246,23 @@ class POCKETBOOK602(USBMS): VENDOR_NAME = '' WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = ['PB602', 'PB902'] +class POCKETBOOK701(USBMS): + + name = 'PocketBook 701 Device Interface' + description = _('Communicate with the PocketBook 701') + author = _('Kovid Goyal') + + supported_platforms = ['windows', 'osx', 'linux'] + FORMATS = ['epub', 'fb2', 'prc', 'mobi', 'pdf', 'djvu', 'rtf', 'chm', + 'doc', 'tcr', 'txt'] + + EBOOK_DIR_MAIN = 'books' + SUPPORTS_SUB_DIRS = True + + VENDOR_ID = [0x18d1] + PRODUCT_ID = [0xa004] + BCD = [0x0224] + + VENDOR_NAME = 'ANDROID' + WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = '__UMS_COMPOSITE' + From 9b7ebac28ae27eb5a2deb0742e9f4d4d630549ef Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 16 Dec 2010 09:31:54 -0700 Subject: [PATCH 11/15] E-book viewer: Page flip animation is used when transitioning between flows as well --- src/calibre/gui2/viewer/documentview.py | 36 ++++++++++++++++++++++--- src/calibre/gui2/viewer/flip.py | 4 +++ 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/src/calibre/gui2/viewer/documentview.py b/src/calibre/gui2/viewer/documentview.py index 4ce9b9818e..21c81bea2f 100644 --- a/src/calibre/gui2/viewer/documentview.py +++ b/src/calibre/gui2/viewer/documentview.py @@ -468,6 +468,7 @@ class DocumentView(QWebView): QWebView.__init__(self, *args) self.flipper = SlideFlip(self) self.is_auto_repeat_event = False + self.load_flip_direction = None self.debug_javascript = False self.shortcuts = Shortcuts(SHORTCUTS, 'shortcuts/viewer') self.self_closing_pat = re.compile(r'<([a-z1-6]+)\s+([^>]+)/>', @@ -646,6 +647,8 @@ class DocumentView(QWebView): return '<%s %s>'%(match.group(1), match.group(2), match.group(1)) def load_path(self, path, pos=0.0): + if self.load_flip_direction is not None: + self.flipper.initialize(self.current_page_image()) self.initial_pos = pos mt = getattr(path, 'mime_type', None) if mt is None: @@ -708,6 +711,11 @@ class DocumentView(QWebView): self.manager.scrolled(self.document.scroll_fraction) self.turn_off_internal_scrollbars() + if self.load_flip_direction is not None: + self.flipper(self.current_page_image(), + duration=self.document.page_flip_duration, + forwards=self.load_flip_direction == 'next') + self.load_flip_direction = None def turn_off_internal_scrollbars(self): self.document.mainFrame().setScrollBarPolicy(Qt.Vertical, Qt.ScrollBarAlwaysOff) @@ -743,13 +751,19 @@ class DocumentView(QWebView): def previous_page(self): if self.flipper.running and not self.is_auto_repeat_event: return + if self.loading_url is not None: + return epf = self.document.enable_page_flip and not self.is_auto_repeat_event delta_y = self.document.window_height - 25 if self.document.at_top: if self.manager is not None: self.to_bottom = True - self.manager.previous_document() + self.load_flip_direction = 'previous' if epf else None + try: + self.manager.previous_document() + finally: + self.load_flip_direction = None else: opos = self.document.ypos upper_limit = opos - delta_y @@ -768,6 +782,8 @@ class DocumentView(QWebView): def next_page(self): if self.flipper.running and not self.is_auto_repeat_event: return + if self.loading_url is not None: + return epf = self.document.enable_page_flip and not self.is_auto_repeat_event window_height = self.document.window_height @@ -779,7 +795,11 @@ class DocumentView(QWebView): delta_y = window_height - 25 if self.document.at_bottom or ddelta <= 0: if self.manager is not None: - self.manager.next_document() + self.load_flip_direction = 'next' if epf else None + try: + self.manager.next_document() + finally: + self.load_flip_direction = None elif ddelta < 25: self.scroll_by(y=ddelta) return @@ -791,7 +811,11 @@ class DocumentView(QWebView): #print 'After set padding=0:', self.document.ypos if opos < oopos: if self.manager is not None: - self.manager.next_document() + self.load_flip_direction = 'next' if epf else None + try: + self.manager.next_document() + finally: + self.load_flip_direction = None return lower_limit = opos + delta_y # Max value of top y co-ord after scrolling max_y = self.document.height - window_height # The maximum possible top y co-ord @@ -799,7 +823,11 @@ class DocumentView(QWebView): padding = lower_limit - max_y if padding == window_height: if self.manager is not None: - self.manager.next_document() + self.load_flip_direction = 'next' if epf else None + try: + self.manager.next_document() + finally: + self.load_flip_direction = 'next' return #print 'Setting padding to:', lower_limit - max_y self.document.set_bottom_padding(lower_limit - max_y) diff --git a/src/calibre/gui2/viewer/flip.py b/src/calibre/gui2/viewer/flip.py index d12d3f56fe..0907f90700 100644 --- a/src/calibre/gui2/viewer/flip.py +++ b/src/calibre/gui2/viewer/flip.py @@ -10,6 +10,8 @@ from PyQt4.Qt import QWidget, QPainter, QPropertyAnimation, QEasingCurve, \ class SlideFlip(QWidget): + # API {{{ + def __init__(self, parent): QWidget.__init__(self, parent) @@ -54,6 +56,8 @@ class SlideFlip(QWidget): self.animation.setDuration(duration * 1000) self.animation.start() + # }}} + def finished(self): self.setVisible(False) self.before_image = self.after_image = None From a6bebd5d755d3e096b5a81dde5f89a0945b6a530 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 16 Dec 2010 09:57:00 -0700 Subject: [PATCH 12/15] More robust flip animation --- src/calibre/gui2/viewer/documentview.py | 25 ++++++++++++++----------- src/calibre/gui2/viewer/flip.py | 6 +++--- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/calibre/gui2/viewer/documentview.py b/src/calibre/gui2/viewer/documentview.py index 21c81bea2f..e63568ce74 100644 --- a/src/calibre/gui2/viewer/documentview.py +++ b/src/calibre/gui2/viewer/documentview.py @@ -648,7 +648,9 @@ class DocumentView(QWebView): def load_path(self, path, pos=0.0): if self.load_flip_direction is not None: - self.flipper.initialize(self.current_page_image()) + self.flipper.initialize(self.current_page_image(), + self.load_flip_direction=='next') + self.load_flip_direction = None self.initial_pos = pos mt = getattr(path, 'mime_type', None) if mt is None: @@ -711,11 +713,13 @@ class DocumentView(QWebView): self.manager.scrolled(self.document.scroll_fraction) self.turn_off_internal_scrollbars() - if self.load_flip_direction is not None: - self.flipper(self.current_page_image(), - duration=self.document.page_flip_duration, - forwards=self.load_flip_direction == 'next') - self.load_flip_direction = None + if self.flipper.isVisible(): + if self.flipper.running: + self.flipper.setVisible(False) + else: + self.flipper(self.current_page_image(), + duration=self.document.page_flip_duration) + def turn_off_internal_scrollbars(self): self.document.mainFrame().setScrollBarPolicy(Qt.Vertical, Qt.ScrollBarAlwaysOff) @@ -771,10 +775,11 @@ class DocumentView(QWebView): upper_limit = 0 if upper_limit < opos: if epf: - self.flipper.initialize(self.current_page_image()) + self.flipper.initialize(self.current_page_image(), + forwards=False) self.document.scroll_to(self.document.xpos, upper_limit) if epf: - self.flipper(self.current_page_image(), forwards=False, + self.flipper(self.current_page_image(), duration=self.document.page_flip_duration) if self.manager is not None: self.manager.scrolled(self.scroll_fraction) @@ -831,10 +836,8 @@ class DocumentView(QWebView): return #print 'Setting padding to:', lower_limit - max_y self.document.set_bottom_padding(lower_limit - max_y) - before_img = None if epf: - before_img = self.current_page_image() - self.flipper.initialize(before_img) + self.flipper.initialize(self.current_page_image()) #print 'Document height:', self.document.height max_y = self.document.height - window_height lower_limit = min(max_y, lower_limit) diff --git a/src/calibre/gui2/viewer/flip.py b/src/calibre/gui2/viewer/flip.py index 0907f90700..4be0657ee9 100644 --- a/src/calibre/gui2/viewer/flip.py +++ b/src/calibre/gui2/viewer/flip.py @@ -29,16 +29,16 @@ class SlideFlip(QWidget): def running(self): return self.animation.state() == self.animation.Running - def initialize(self, image): + def initialize(self, image, forwards=True): + self.flip_forwards = forwards self.before_image = QPixmap.fromImage(image) self.after_image = None self.setGeometry(0, 0, image.width(), image.height()) self.setVisible(True) - def __call__(self, image, duration=0.5, forwards=True): + def __call__(self, image, duration=0.5): if self.running: return - self.flip_forwards = forwards self.after_image = QPixmap.fromImage(image) if self.flip_forwards: From 6a6b2a6f58285c5a5757f1c24280d9568b1c7b67 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 16 Dec 2010 10:05:42 -0700 Subject: [PATCH 13/15] ... --- src/calibre/gui2/viewer/documentview.py | 35 ++++++++----------------- src/calibre/gui2/viewer/flip.py | 18 +++++++++++++ 2 files changed, 29 insertions(+), 24 deletions(-) diff --git a/src/calibre/gui2/viewer/documentview.py b/src/calibre/gui2/viewer/documentview.py index e63568ce74..f131dd522d 100644 --- a/src/calibre/gui2/viewer/documentview.py +++ b/src/calibre/gui2/viewer/documentview.py @@ -468,7 +468,6 @@ class DocumentView(QWebView): QWebView.__init__(self, *args) self.flipper = SlideFlip(self) self.is_auto_repeat_event = False - self.load_flip_direction = None self.debug_javascript = False self.shortcuts = Shortcuts(SHORTCUTS, 'shortcuts/viewer') self.self_closing_pat = re.compile(r'<([a-z1-6]+)\s+([^>]+)/>', @@ -647,10 +646,6 @@ class DocumentView(QWebView): return '<%s %s>'%(match.group(1), match.group(2), match.group(1)) def load_path(self, path, pos=0.0): - if self.load_flip_direction is not None: - self.flipper.initialize(self.current_page_image(), - self.load_flip_direction=='next') - self.load_flip_direction = None self.initial_pos = pos mt = getattr(path, 'mime_type', None) if mt is None: @@ -763,11 +758,9 @@ class DocumentView(QWebView): if self.document.at_top: if self.manager is not None: self.to_bottom = True - self.load_flip_direction = 'previous' if epf else None - try: + if epf: + self.flipper.initialize(self.current_page_image(), False) self.manager.previous_document() - finally: - self.load_flip_direction = None else: opos = self.document.ypos upper_limit = opos - delta_y @@ -800,11 +793,9 @@ class DocumentView(QWebView): delta_y = window_height - 25 if self.document.at_bottom or ddelta <= 0: if self.manager is not None: - self.load_flip_direction = 'next' if epf else None - try: - self.manager.next_document() - finally: - self.load_flip_direction = None + if epf: + self.flipper.initialize(self.current_page_image()) + self.manager.next_document() elif ddelta < 25: self.scroll_by(y=ddelta) return @@ -816,11 +807,9 @@ class DocumentView(QWebView): #print 'After set padding=0:', self.document.ypos if opos < oopos: if self.manager is not None: - self.load_flip_direction = 'next' if epf else None - try: - self.manager.next_document() - finally: - self.load_flip_direction = None + if epf: + self.flipper.initialize(self.current_page_image()) + self.manager.next_document() return lower_limit = opos + delta_y # Max value of top y co-ord after scrolling max_y = self.document.height - window_height # The maximum possible top y co-ord @@ -828,11 +817,9 @@ class DocumentView(QWebView): padding = lower_limit - max_y if padding == window_height: if self.manager is not None: - self.load_flip_direction = 'next' if epf else None - try: - self.manager.next_document() - finally: - self.load_flip_direction = 'next' + if epf: + self.flipper.initialize(self.current_page_image()) + self.manager.next_document() return #print 'Setting padding to:', lower_limit - max_y self.document.set_bottom_padding(lower_limit - max_y) diff --git a/src/calibre/gui2/viewer/flip.py b/src/calibre/gui2/viewer/flip.py index 4be0657ee9..5432909b2b 100644 --- a/src/calibre/gui2/viewer/flip.py +++ b/src/calibre/gui2/viewer/flip.py @@ -12,6 +12,8 @@ class SlideFlip(QWidget): # API {{{ + # In addition the isVisible() and setVisible() methods must be present + def __init__(self, parent): QWidget.__init__(self, parent) @@ -27,9 +29,19 @@ class SlideFlip(QWidget): @property def running(self): + 'True iff animation is currently running' return self.animation.state() == self.animation.Running def initialize(self, image, forwards=True): + ''' + Initialize the flipper, causes the flipper to show itself displaying + the full `image`. + + :param image: The image to display as background + :param forwards: If True flipper will flip forwards, otherwise + backwards + + ''' self.flip_forwards = forwards self.before_image = QPixmap.fromImage(image) self.after_image = None @@ -37,6 +49,12 @@ class SlideFlip(QWidget): self.setVisible(True) def __call__(self, image, duration=0.5): + ''' + Start the animation. You must have called :meth:`initialize` first. + + :param duration: Animation duration in seconds. + + ''' if self.running: return self.after_image = QPixmap.fromImage(image) From 5ce7afa6e2fb0743e3c7bba2de33d76bd016d687 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 16 Dec 2010 10:57:15 -0700 Subject: [PATCH 14/15] ... --- src/calibre/ebooks/metadata/pdf.py | 1 + src/calibre/utils/mem.py | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/src/calibre/ebooks/metadata/pdf.py b/src/calibre/ebooks/metadata/pdf.py index 2d1935539e..20a4c4659e 100644 --- a/src/calibre/ebooks/metadata/pdf.py +++ b/src/calibre/ebooks/metadata/pdf.py @@ -17,6 +17,7 @@ pdfreflow, pdfreflow_error = plugins['pdfreflow'] def get_metadata(stream, cover=True): if pdfreflow is None: raise RuntimeError(pdfreflow_error) + stream.seek(0) raw = stream.read() #isbn = _isbn_pat.search(raw) #if isbn is not None: diff --git a/src/calibre/utils/mem.py b/src/calibre/utils/mem.py index f48aec34c6..1f9bff8d63 100644 --- a/src/calibre/utils/mem.py +++ b/src/calibre/utils/mem.py @@ -5,6 +5,8 @@ __license__ = 'GPL v3' __copyright__ = '2010, Kovid Goyal ' __docformat__ = 'restructuredtext en' +import gc + ## {{{ http://code.activestate.com/recipes/286222/ (r1) import os @@ -52,4 +54,19 @@ def stacksize(since=0.0): ## end of http://code.activestate.com/recipes/286222/ }}} +def gc_histogram(): + """Returns per-class counts of existing objects.""" + result = {} + for o in gc.get_objects(): + t = type(o) + count = result.get(t, 0) + result[t] = count + 1 + return result + +def diff_hists(h1, h2): + """Prints differences between two results of gc_histogram().""" + for k in h1: + if h1[k] != h2[k]: + print "%s: %d -> %d (%s%d)" % ( + k, h1[k], h2[k], h2[k] > h1[k] and "+" or "", h2[k] - h1[k]) From 8289d684543a54a5a0f1c6446f4ec56e777af5c8 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 16 Dec 2010 12:13:40 -0700 Subject: [PATCH 15/15] Fix #7917 (New Scientist recipe update) --- resources/recipes/new_scientist.recipe | 52 ++++++++++++++++---------- 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/resources/recipes/new_scientist.recipe b/resources/recipes/new_scientist.recipe index 02bbbe4d42..434c41f525 100644 --- a/resources/recipes/new_scientist.recipe +++ b/resources/recipes/new_scientist.recipe @@ -5,6 +5,7 @@ newscientist.com ''' import re +import urllib from calibre.web.feeds.news import BasicNewsRecipe class NewScientist(BasicNewsRecipe): @@ -24,7 +25,7 @@ class NewScientist(BasicNewsRecipe): needs_subscription = 'optional' extra_css = """ body{font-family: Arial,sans-serif} - img{margin-bottom: 0.8em} + img{margin-bottom: 0.8em; display: block} .quotebx{font-size: x-large; font-weight: bold; margin-right: 2em; margin-left: 2em} """ @@ -41,12 +42,14 @@ class NewScientist(BasicNewsRecipe): def get_browser(self): br = BasicNewsRecipe.get_browser() br.open('http://www.newscientist.com/') - if self.username is not None and self.password is not None: - br.open('https://www.newscientist.com/user/login?redirectURL=') - br.select_form(nr=2) - br['loginId' ] = self.username - br['password'] = self.password - br.submit() + if self.username is not None and self.password is not None: + br.open('https://www.newscientist.com/user/login') + data = urllib.urlencode({ 'source':'form' + ,'redirectURL':'' + ,'loginId':self.username + ,'password':self.password + }) + br.open('https://www.newscientist.com/user/login',data) return br remove_tags = [ @@ -55,21 +58,22 @@ class NewScientist(BasicNewsRecipe): ,dict(name='p' , attrs={'class':['marker','infotext' ]}) ,dict(name='meta' , attrs={'name' :'description' }) ,dict(name='a' , attrs={'rel' :'tag' }) + ,dict(name='ul' , attrs={'class':'markerlist' }) ,dict(name=['link','base','meta','iframe','object','embed']) ] remove_tags_after = dict(attrs={'class':['nbpcopy','comments']}) - remove_attributes = ['height','width','lang'] + remove_attributes = ['height','width','lang','onclick'] feeds = [ - (u'Latest Headlines' , u'http://feeds.newscientist.com/science-news' ) - ,(u'Magazine' , u'http://www.newscientist.com/feed/magazine' ) - ,(u'Health' , u'http://www.newscientist.com/feed/view?id=2&type=channel' ) - ,(u'Life' , u'http://www.newscientist.com/feed/view?id=3&type=channel' ) - ,(u'Space' , u'http://www.newscientist.com/feed/view?id=6&type=channel' ) - ,(u'Physics and Mathematics' , u'http://www.newscientist.com/feed/view?id=4&type=channel' ) - ,(u'Environment' , u'http://www.newscientist.com/feed/view?id=1&type=channel' ) - ,(u'Science in Society' , u'http://www.newscientist.com/feed/view?id=5&type=channel' ) - ,(u'Tech' , u'http://www.newscientist.com/feed/view?id=7&type=channel' ) + (u'Latest Headlines' , u'http://feeds.newscientist.com/science-news' ) + ,(u'Magazine' , u'http://feeds.newscientist.com/magazine' ) + ,(u'Health' , u'http://feeds.newscientist.com/health' ) + ,(u'Life' , u'http://feeds.newscientist.com/life' ) + ,(u'Space' , u'http://feeds.newscientist.com/space' ) + ,(u'Physics and Mathematics' , u'http://feeds.newscientist.com/physics-math' ) + ,(u'Environment' , u'http://feeds.newscientist.com/environment' ) + ,(u'Science in Society' , u'http://feeds.newscientist.com/science-in-society' ) + ,(u'Tech' , u'http://feeds.newscientist.com/tech' ) ] def get_article_url(self, article): @@ -79,11 +83,21 @@ class NewScientist(BasicNewsRecipe): return url + '?full=true&print=true' def preprocess_html(self, soup): + if soup.html.has_key('id'): + del soup.html['id'] + for item in soup.findAll(style=True): + del item['style'] for item in soup.findAll(['quote','quotetext']): item.name='p' + for item in soup.findAll(['xref','figref']): + tstr = item.string + item.replaceWith(tstr) for tg in soup.findAll('a'): if tg.string == 'Home': tg.parent.extract() - return self.adeify_images(soup) - return self.adeify_images(soup) + else: + if tg.string is not None: + tstr = tg.string + tg.replaceWith(tstr) + return soup