diff --git a/Changelog.yaml b/Changelog.yaml index bde3841981..3e5dbb06fc 100644 --- a/Changelog.yaml +++ b/Changelog.yaml @@ -4,6 +4,14 @@ # for important features/bug fixes. # Also, each release can have new and improved recipes. +- version: 0.7.31 + date: 2010-11-27 + + bug fixes: + - title: "Fix various regressions in the calibre windows build caused by the switch to python 2.7. If you are on windows and upgraded to 0.7.30, it is highly recommended that you upgrade to 0.7.31. If you are not on windows, you can ignore 0.7.31" + tickets: [7685, 7694, 7691] + + - version: 0.7.30 date: 2010-11-26 diff --git a/resources/recipes/new_yorker.recipe b/resources/recipes/new_yorker.recipe index 605b34e79b..1a2091cd52 100644 --- a/resources/recipes/new_yorker.recipe +++ b/resources/recipes/new_yorker.recipe @@ -32,15 +32,15 @@ class NewYorker(BasicNewsRecipe): , 'publisher' : publisher , 'language' : language } - + keep_only_tags = [ dict(name='div', attrs={'class':'headers'}) ,dict(name='div', attrs={'id':['articleheads','items-container','articleRail','articletext','photocredits']}) ] remove_tags = [ dict(name=['meta','iframe','base','link','embed','object']) - ,dict(attrs={'class':['utils','articleRailLinks','icons'] }) - ,dict(attrs={'id':['show-header','show-footer'] }) + ,dict(attrs={'class':['utils','articleRailLinks','icons'] }) + ,dict(attrs={'id':['show-header','show-footer'] }) ] remove_attributes = ['lang'] feeds = [(u'The New Yorker', u'http://feeds.newyorker.com/services/rss/feeds/everything.xml')] @@ -58,4 +58,4 @@ class NewYorker(BasicNewsRecipe): if cover_item: cover_url = 'http://www.newyorker.com' + cover_item['src'].strip() return cover_url - \ No newline at end of file + diff --git a/resources/recipes/revista_muy.recipe b/resources/recipes/revista_muy.recipe index b101fe97ce..c4516f718d 100644 --- a/resources/recipes/revista_muy.recipe +++ b/resources/recipes/revista_muy.recipe @@ -13,6 +13,8 @@ class RevistaMuyInteresante(BasicNewsRecipe): no_stylesheets = True remove_javascript = True + conversion_options = {'linearize_tables': True} + extra_css = ' .txt_articulo{ font-family: sans-serif; font-size: medium; text-align: justify } .contentheading{font-family: serif; font-size: large; font-weight: bold; color: #000000; text-align: center}' @@ -39,11 +41,12 @@ class RevistaMuyInteresante(BasicNewsRecipe): keep_only_tags = [dict(name='div', attrs={'class':['article']}),dict(name='td', attrs={'class':['txt_articulo']})] remove_tags = [ - dict(name=['object','link','script','ul']) + dict(name=['object','link','script','ul','iframe','ins']) ,dict(name='div', attrs={'id':['comment']}) ,dict(name='td', attrs={'class':['buttonheading']}) - ,dict(name='div', attrs={'class':['tags_articles']}) + ,dict(name='div', attrs={'class':['tags_articles','bajo_title']}) ,dict(name='table', attrs={'class':['pagenav']}) + ,dict(name='form', attrs={'class':['voteform']}) ] remove_tags_after = dict(name='div', attrs={'class':'tags_articles'}) @@ -115,3 +118,5 @@ class RevistaMuyInteresante(BasicNewsRecipe): if link_item: cover_url = "http://www.muyinteresante.es"+link_item['src'] return cover_url + + diff --git a/resources/recipes/rusiahoy.recipe b/resources/recipes/rusiahoy.recipe index 326c1695b0..8dfa55eece 100644 --- a/resources/recipes/rusiahoy.recipe +++ b/resources/recipes/rusiahoy.recipe @@ -19,7 +19,7 @@ class RusiaHoy(BasicNewsRecipe): use_embedded_content = False language = 'es' remove_empty_feeds = True - extra_css = """ + extra_css = """ body{font-family: Arial,sans-serif } .article_article_title{font-size: xx-large; font-weight: bold} .article_date{color: black; font-size: small} @@ -44,4 +44,4 @@ class RusiaHoy(BasicNewsRecipe): for item in soup.findAll(style=True): del item['style'] return soup - \ No newline at end of file + diff --git a/resources/recipes/the_workingham_times.recipe b/resources/recipes/the_workingham_times.recipe index ad069f1e9b..8d18fb42a0 100644 --- a/resources/recipes/the_workingham_times.recipe +++ b/resources/recipes/the_workingham_times.recipe @@ -6,8 +6,8 @@ www.getwokingham.co.uk from calibre.web.feeds.recipes import BasicNewsRecipe -class TheWorkinghamTimes(BasicNewsRecipe): - title = 'The Workingham Times' +class TheWokinghamTimes(BasicNewsRecipe): + title = 'The Wokingham Times' __author__ = 'Darko Miletic' description = 'News from UK' oldest_article = 2 diff --git a/setup/installer/windows/freeze.py b/setup/installer/windows/freeze.py index 118b6690f0..30cc2a97af 100644 --- a/setup/installer/windows/freeze.py +++ b/setup/installer/windows/freeze.py @@ -108,8 +108,7 @@ class Win32Freeze(Command, WixMixIn): for f in x[-1]: if f.lower().endswith('.dll'): f = self.j(x[0], f) - if 'py2exe' not in f: - shutil.copy2(f, self.dll_dir) + shutil.copy2(f, self.dll_dir) shutil.copy2( r'C:\Python%(v)s\Lib\site-packages\pywin32_system32\pywintypes%(v)s.dll' % dict(v=self.py_ver), self.dll_dir) @@ -118,7 +117,7 @@ class Win32Freeze(Command, WixMixIn): ans = [] for x in items: ext = os.path.splitext(x)[1] - if (not ext and (x in ('demos', 'tests') or 'py2exe' in x)) or \ + if (not ext and (x in ('demos', 'tests'))) or \ (ext in ('.dll', '.chm', '.htm', '.txt')): ans.append(x) return ans diff --git a/setup/installer/windows/notes.rst b/setup/installer/windows/notes.rst index c45cd4cfc9..ed78bf3158 100644 --- a/setup/installer/windows/notes.rst +++ b/setup/installer/windows/notes.rst @@ -21,6 +21,8 @@ This is where all dependencies will be installed. Add C:\Python27\Scripts and C:\Python27 to PATH +Edit mimetypes.py in C:\Python27\Lib and change line 250 UnicodeEncodeError to ValueError and set _winreg = None to prevent reading of mimetypes from the windows registry + Install setuptools from http://pypi.python.org/pypi/setuptools If there are no windows binaries already compiled for the version of python you are using then download the source and run the following command in the folder where the source has been unpacked:: @@ -32,6 +34,8 @@ Run the following command to install python dependencies:: Install BeautifulSoup 3.0.x manually into site-packages (3.1.x parses broken HTML very poorly) +Install pywin32 and edit win32com\__init__.py setting _frozen = True and +__gen_path__ to a temp dir (otherwise it tries to set it to a dir in the install tree which leads to permission errors) SQLite --------- @@ -66,7 +70,11 @@ Compiling instructions:: Python Imaging Library ------------------------ -Install as normal using provided installer. +Install as normal using installer at http://www.lfd.uci.edu/~gohlke/pythonlibs/ + +Test it on the target system with + +calibre-debug -c "import _imaging, _imagingmath, _imagingft, _imagingcms" Libunrar ---------- diff --git a/src/calibre/__init__.py b/src/calibre/__init__.py index a43b0126ff..688d7793a4 100644 --- a/src/calibre/__init__.py +++ b/src/calibre/__init__.py @@ -48,6 +48,13 @@ mimetypes.add_type('application/x-cbz', '.cbz') mimetypes.add_type('application/x-cbr', '.cbr') mimetypes.add_type('application/x-koboreader-ebook', '.kobo') mimetypes.add_type('image/wmf', '.wmf') +mimetypes.add_type('image/jpeg', '.jpg') +mimetypes.add_type('image/jpeg', '.jpeg') +mimetypes.add_type('image/png', '.png') +mimetypes.add_type('image/gif', '.gif') +mimetypes.add_type('image/bmp', '.bmp') +mimetypes.add_type('image/svg+xml', '.svg') + guess_type = mimetypes.guess_type import cssutils cssutils.log.setLevel(logging.WARN) @@ -362,6 +369,8 @@ def walk(dir): def strftime(fmt, t=None): ''' A version of strftime that returns unicode strings and tries to handle dates before 1900 ''' + if not fmt: + return u'' if t is None: t = time.localtime() if hasattr(t, 'timetuple'): @@ -378,7 +387,8 @@ def strftime(fmt, t=None): if isinstance(fmt, unicode): fmt = fmt.encode('mbcs') ans = plugins['winutil'][0].strftime(fmt, t) - ans = time.strftime(fmt, t).decode(preferred_encoding, 'replace') + else: + ans = time.strftime(fmt, t).decode(preferred_encoding, 'replace') if early_year: ans = ans.replace('_early year hack##', str(orig_year)) return ans diff --git a/src/calibre/constants.py b/src/calibre/constants.py index ba597fcc30..fc33067f49 100644 --- a/src/calibre/constants.py +++ b/src/calibre/constants.py @@ -2,7 +2,7 @@ __license__ = 'GPL v3' __copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net' __docformat__ = 'restructuredtext en' __appname__ = 'calibre' -__version__ = '0.7.30' +__version__ = '0.7.31' __author__ = "Kovid Goyal " import re diff --git a/src/calibre/customize/__init__.py b/src/calibre/customize/__init__.py index 523fc51d92..b4e9b6c448 100644 --- a/src/calibre/customize/__init__.py +++ b/src/calibre/customize/__init__.py @@ -370,6 +370,15 @@ class InterfaceActionBase(Plugin): # {{{ can_be_disabled = False actual_plugin = None + + def load_actual_plugin(self, gui): + ''' + This method must return the actual interface action plugin object. + ''' + mod, cls = self.actual_plugin.split(':') + return getattr(__import__(mod, fromlist=['1'], level=0), cls)(gui, + self.site_customization) + # }}} class PreferencesPlugin(Plugin): # {{{ diff --git a/src/calibre/ebooks/rtf/input.py b/src/calibre/ebooks/rtf/input.py index 75c839eb83..57903a6711 100644 --- a/src/calibre/ebooks/rtf/input.py +++ b/src/calibre/ebooks/rtf/input.py @@ -292,7 +292,8 @@ class RTFInput(InputFormatPlugin): # Replace newlines inserted by the 'empty_paragraphs' option in rtf2xml with html blank lines if not getattr(self.options, 'remove_paragraph_spacing', False): res = re.sub('\s*', '', res) - res = re.sub('(?<=\n)\n{2}', u'

\u00a0

\n', res) + res = re.sub('(?<=\n)\n{2}', + u'

\u00a0

\n'.encode('utf-8'), res) if self.options.preprocess_html: preprocessor = PreProcessor(self.options, log=getattr(self, 'log', None)) res = preprocessor(res) diff --git a/src/calibre/gui2/book_details.py b/src/calibre/gui2/book_details.py index e193fe10b2..4ffc8da650 100644 --- a/src/calibre/gui2/book_details.py +++ b/src/calibre/gui2/book_details.py @@ -190,6 +190,10 @@ class BookInfo(QWebView): self.page().setLinkDelegationPolicy(self.page().DelegateAllLinks) self.linkClicked.connect(self.link_activated) self._link_clicked = False + self.setAttribute(Qt.WA_OpaquePaintEvent, False) + palette = self.palette() + palette.setBrush(QPalette.Base, Qt.transparent) + self.page().setPalette(palette) def link_activated(self, link): self._link_clicked = True @@ -210,16 +214,23 @@ class BookInfo(QWebView): def _show_data(self, rows, comments): + + def color_to_string(col): + ans = '#000000' + if col.isValid(): + col = col.toRgb() + if col.isValid(): + ans = unicode(col.name()) + return ans + f = QFontInfo(QApplication.font(self.parent())).pixelSize() - p = unicode(QApplication.palette().color(QPalette.Normal, - QPalette.Window).name()) - c = unicode(QApplication.palette().color(QPalette.Normal, - QPalette.WindowText).name()) + c = color_to_string(QApplication.palette().color(QPalette.Normal, + QPalette.WindowText)) templ = u'''\ @@ -227,7 +238,7 @@ class BookInfo(QWebView): %%s - '''%(p, f, c) + '''%(f, c) if self.vertical: if comments: rows += u'%s'%comments diff --git a/src/calibre/gui2/ui.py b/src/calibre/gui2/ui.py index bd0743e819..00bba2b491 100644 --- a/src/calibre/gui2/ui.py +++ b/src/calibre/gui2/ui.py @@ -102,9 +102,7 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{ self.device_connected = None acmap = OrderedDict() for action in interface_actions(): - mod, cls = action.actual_plugin.split(':') - ac = getattr(__import__(mod, fromlist=['1'], level=0), cls)(self, - action.site_customization) + ac = action.load_actual_plugin(self) if ac.name in acmap: if ac.priority >= acmap[ac.name].priority: acmap[ac.name] = ac diff --git a/src/calibre/library/sqlite.py b/src/calibre/library/sqlite.py index 94338433a5..8aa76b2643 100644 --- a/src/calibre/library/sqlite.py +++ b/src/calibre/library/sqlite.py @@ -150,7 +150,7 @@ class DBThread(Thread): detect_types=sqlite.PARSE_DECLTYPES|sqlite.PARSE_COLNAMES) self.conn.execute('pragma cache_size=5000') encoding = self.conn.execute('pragma encoding').fetchone()[0] - c_ext_loaded = False #load_c_extensions(self.conn) + c_ext_loaded = load_c_extensions(self.conn) self.conn.row_factory = sqlite.Row if self.row_factory else lambda cursor, row : list(row) self.conn.create_aggregate('concat', 1, Concatenate) if not c_ext_loaded: diff --git a/src/calibre/manual/customize.rst b/src/calibre/manual/customize.rst index e0f799f572..0d6f75b9cf 100644 --- a/src/calibre/manual/customize.rst +++ b/src/calibre/manual/customize.rst @@ -98,6 +98,44 @@ Every time you use calibre to convert a book, the plugin's :meth:`run` method wi converted book will have its publisher set to "Hello World". For more information about |app|'s plugin system, read on... + +A Hello World GUI plugin +--------------------------- + +Here's a simple Hello World plugin for the |app| GUI. It will cause a box to popup with the message "Hellooo World!" when you press Ctrl+Shift+H + +.. code-block:: python + + from calibre.customize import InterfaceActionBase + + class HelloWorldBase(InterfaceActionBase): + + name = 'Hello World GUI' + author = 'The little green man' + + def load_actual_plugin(self, gui): + from calibre.gui2.actions import InterfaceAction + + class HelloWorld(InterfaceAction): + name = 'Hello World GUI' + action_spec = ('Hello World!', 'add_book.png', None, + _('Ctrl+Shift+H')) + + def genesis(self): + self.qaction.triggered.connect(self.hello_world) + + def hello_world(self, *args): + from calibre.gui2 import info_dialog + info_dialog(self.gui, 'Hello World!', 'Hellooo World!', + show=True) + + return HelloWorld(gui, self.site_customization) + +You can also have it show up in the toolbars/context menu by going to Preferences->Toolbars and adding this plugin to the locations you want it to be in. + +While this plugin is utterly useless, note that all calibre GUI actions like adding/saving/removing/viewing/etc. are implemented as plugins, so there is no limit to what you can achieve. The key thing to remember is that the plugin has access to the full |app| GUI via ``self.gui``. + + The Plugin base class ------------------------ diff --git a/src/calibre/manual/plugins.rst b/src/calibre/manual/plugins.rst index eb955aebee..0a62218fb9 100644 --- a/src/calibre/manual/plugins.rst +++ b/src/calibre/manual/plugins.rst @@ -161,11 +161,20 @@ The base class for such devices is :class:`calibre.devices.usbms.driver.USBMS`. User Interface Actions -------------------------- +If you are adding your own plugin in a zip file, you should subclass both InterfaceActionBase and InterfaceAction. The :meth:`load_actual_plugin` method of you InterfaceActionBase subclass must return an instantiated object of your InterfaceBase subclass. + + .. autoclass:: calibre.gui2.actions.InterfaceAction :show-inheritance: :members: :member-order: bysource +.. autoclass:: calibre.customize.InterfaceActionBase + :show-inheritance: + :members: + :member-order: bysource + + Preferences Plugins -------------------------- diff --git a/src/calibre/translations/calibre.pot b/src/calibre/translations/calibre.pot index 262d5915ec..c513e267e3 100644 --- a/src/calibre/translations/calibre.pot +++ b/src/calibre/translations/calibre.pot @@ -4,9 +4,9 @@ # msgid "" msgstr "" -"Project-Id-Version: calibre 0.7.30\n" -"POT-Creation-Date: 2010-11-26 10:43+MST\n" -"PO-Revision-Date: 2010-11-26 10:43+MST\n" +"Project-Id-Version: calibre 0.7.31\n" +"POT-Creation-Date: 2010-11-27 11:31+MST\n" +"PO-Revision-Date: 2010-11-27 11:31+MST\n" "Last-Translator: Automatically generated\n" "Language-Team: LANGUAGE\n" "MIME-Version: 1.0\n"