From 2e8e0f6daf05b9395d738e2e1836f89158ba8e6b Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Fri, 22 Feb 2013 18:48:33 +0530
Subject: [PATCH 01/31] Finish writing one-one fields
---
src/calibre/db/tests/writing.py | 32 ++++++++++++++++++++++++++++----
src/calibre/db/write.py | 10 +++++++++-
2 files changed, 37 insertions(+), 5 deletions(-)
diff --git a/src/calibre/db/tests/writing.py b/src/calibre/db/tests/writing.py
index 7b7e815587..f6cf4d2d45 100644
--- a/src/calibre/db/tests/writing.py
+++ b/src/calibre/db/tests/writing.py
@@ -41,11 +41,11 @@ class WritingTest(BaseTest):
self.create_setter(name, setter))
def run_tests(self, tests):
- cl = self.cloned_library
results = {}
for test in tests:
results[test] = []
for val in test.vals:
+ cl = self.cloned_library
cache = self.init_cache(cl)
cache.set_field(test.name, {1: val})
cached_res = cache.field_for(test.name, 1)
@@ -65,11 +65,16 @@ class WritingTest(BaseTest):
test.name, old_sqlite_res, sqlite_res))
del db
-
-
def test_one_one(self):
'Test setting of values in one-one fields'
- tests = []
+ tests = [self.create_test('#yesno', (True, False, 'true', 'false', None))]
+ for name, getter, setter in (
+ ('series_index', 'series_index', 'set_series_index'),
+ ('#float', None, None),
+ ):
+ vals = ['1.5', None, 0, 1.0]
+ tests.append(self.create_test(name, tuple(vals), getter, setter))
+
for name, getter, setter in (
('pubdate', 'pubdate', 'set_pubdate'),
('timestamp', 'timestamp', 'set_timestamp'),
@@ -78,6 +83,25 @@ class WritingTest(BaseTest):
tests.append(self.create_test(
name, ('2011-1-12', UNDEFINED_DATE, None), getter, setter))
+ for name, getter, setter in (
+ ('title', 'title', 'set_title'),
+ ('uuid', 'uuid', 'set_uuid'),
+ ('author_sort', 'author_sort', 'set_author_sort'),
+ ('sort', 'title_sort', 'set_title_sort'),
+ ('#comments', None, None),
+ ('comments', 'comments', 'set_comment'),
+ ):
+ vals = ['something', None]
+ if name not in {'comments', '#comments'}:
+ # Setting text column to '' returns None in the new backend
+ # and '' in the old. I think None is more correct.
+ vals.append('')
+ if name == 'comments':
+ # Again new behavior of deleting comment rather than setting
+ # empty string is more correct.
+ vals.remove(None)
+ tests.append(self.create_test(name, tuple(vals), getter, setter))
+
self.run_tests(tests)
def tests():
diff --git a/src/calibre/db/write.py b/src/calibre/db/write.py
index 14ddea8dfb..b3449f1387 100644
--- a/src/calibre/db/write.py
+++ b/src/calibre/db/write.py
@@ -98,10 +98,14 @@ def get_adapter(name, metadata):
if name == 'title':
return lambda x: ans(x) or _('Unknown')
+ if name == 'author_sort':
+ return lambda x: ans(x) or ''
if name == 'authors':
return lambda x: ans(x) or (_('Unknown'),)
if name in {'timestamp', 'last_modified'}:
return lambda x: ans(x) or UNDEFINED_DATE
+ if name == 'series_index':
+ return lambda x: 1.0 if ans(x) is None else ans(x)
return ans
# }}}
@@ -148,13 +152,17 @@ class Writer(object):
if dt == 'composite' or field.name in {
'id', 'cover', 'size', 'path', 'formats', 'news'}:
self.set_books_func = dummy
+ elif self.name[0] == '#' and self.name.endswith('_index'):
+ # TODO: Implement this
+ pass
elif field.is_many:
# TODO: Implement this
pass
+ # TODO: Remember to change commas to | when writing authors to sqlite
else:
self.set_books_func = (one_one_in_books if field.metadata['table']
== 'books' else one_one_in_other)
- if self.name in {'timestamp', 'uuid'}:
+ if self.name in {'timestamp', 'uuid', 'sort'}:
self.accept_vals = bool
def set_books(self, book_id_val_map, db):
From 836e00211a4a6a24abe80efdc5b3242be49b6b4d Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Fri, 22 Feb 2013 19:45:12 +0530
Subject: [PATCH 02/31] Implement writing custom series index fields
---
src/calibre/db/cache.py | 6 ++++++
src/calibre/db/fields.py | 1 +
src/calibre/db/tests/writing.py | 33 ++++++++++++++++++++++-----------
src/calibre/db/write.py | 18 ++++++++++++++++--
4 files changed, 45 insertions(+), 13 deletions(-)
diff --git a/src/calibre/db/cache.py b/src/calibre/db/cache.py
index 236d25c3e9..af22db64d7 100644
--- a/src/calibre/db/cache.py
+++ b/src/calibre/db/cache.py
@@ -211,6 +211,12 @@ class Cache(object):
self.fields['ondevice'] = create_field('ondevice',
VirtualTable('ondevice'))
+ for name, field in self.fields.iteritems():
+ if name[0] == '#' and name.endswith('_index'):
+ field.series_field = self.fields[name[:-len('_index')]]
+ elif name == 'series_index':
+ field.series_field = self.fields['series']
+
@read_api
def field_for(self, name, book_id, default_value=None):
'''
diff --git a/src/calibre/db/fields.py b/src/calibre/db/fields.py
index 46386b9ba5..dee312dbcd 100644
--- a/src/calibre/db/fields.py
+++ b/src/calibre/db/fields.py
@@ -46,6 +46,7 @@ class Field(object):
elif name == 'languages':
self.category_formatter = calibre_langcode_to_name
self.writer = Writer(self)
+ self.series_field = None
@property
def metadata(self):
diff --git a/src/calibre/db/tests/writing.py b/src/calibre/db/tests/writing.py
index f6cf4d2d45..314d6fbb7a 100644
--- a/src/calibre/db/tests/writing.py
+++ b/src/calibre/db/tests/writing.py
@@ -22,7 +22,11 @@ class WritingTest(BaseTest):
def create_getter(self, name, getter=None):
if getter is None:
- ans = lambda db:partial(db.get_custom, label=name[1:],
+ if name.endswith('_index'):
+ ans = lambda db:partial(db.get_custom_extra, index_is_id=True,
+ label=name[1:].replace('_index', ''))
+ else:
+ ans = lambda db:partial(db.get_custom, label=name[1:],
index_is_id=True)
else:
ans = lambda db:partial(getattr(db, getter), index_is_id=True)
@@ -53,22 +57,29 @@ class WritingTest(BaseTest):
db = self.init_old(cl)
getter = test.getter(db)
sqlite_res = getter(1)
- test.setter(db)(1, val)
- old_cached_res = getter(1)
- self.assertEqual(old_cached_res, cached_res,
- 'Failed setting for %s with value %r, cached value not the same. Old: %r != New: %r'%(
- test.name, val, old_cached_res, cached_res))
- db.refresh()
- old_sqlite_res = getter(1)
- self.assertEqual(old_sqlite_res, sqlite_res,
- 'Failed setting for %s, sqlite value not the same: %r != %r'%(
- test.name, old_sqlite_res, sqlite_res))
+ if test.name.endswith('_index'):
+ val = float(val) if val is not None else 1.0
+ self.assertEqual(sqlite_res, val,
+ 'Failed setting for %s with value %r, sqlite value not the same. val: %r != sqlite_val: %r'%(
+ test.name, val, val, sqlite_res))
+ else:
+ test.setter(db)(1, val)
+ old_cached_res = getter(1)
+ self.assertEqual(old_cached_res, cached_res,
+ 'Failed setting for %s with value %r, cached value not the same. Old: %r != New: %r'%(
+ test.name, val, old_cached_res, cached_res))
+ db.refresh()
+ old_sqlite_res = getter(1)
+ self.assertEqual(old_sqlite_res, sqlite_res,
+ 'Failed setting for %s, sqlite value not the same: %r != %r'%(
+ test.name, old_sqlite_res, sqlite_res))
del db
def test_one_one(self):
'Test setting of values in one-one fields'
tests = [self.create_test('#yesno', (True, False, 'true', 'false', None))]
for name, getter, setter in (
+ ('#series_index', None, None),
('series_index', 'series_index', 'set_series_index'),
('#float', None, None),
):
diff --git a/src/calibre/db/write.py b/src/calibre/db/write.py
index b3449f1387..cb710d271f 100644
--- a/src/calibre/db/write.py
+++ b/src/calibre/db/write.py
@@ -138,6 +138,21 @@ def one_one_in_other(book_id_val_map, db, field, *args):
field.table.book_col_map.update(updated)
return set(book_id_val_map)
+def custom_series_index(book_id_val_map, db, field, *args):
+ series_field = field.series_field
+ sequence = []
+ for book_id, sidx in book_id_val_map.iteritems():
+ if sidx is None:
+ sidx = 1.0
+ ids = series_field.ids_for_book(book_id)
+ if ids:
+ sequence.append((sidx, book_id, ids[0]))
+ field.table.book_col_map[book_id] = sidx
+ if sequence:
+ db.conn.executemany('UPDATE %s SET %s=? WHERE book=? AND value=?'%(
+ field.metadata['table'], field.metadata['column']), sequence)
+ return {s[0] for s in sequence}
+
def dummy(book_id_val_map, *args):
return set()
@@ -153,8 +168,7 @@ class Writer(object):
'id', 'cover', 'size', 'path', 'formats', 'news'}:
self.set_books_func = dummy
elif self.name[0] == '#' and self.name.endswith('_index'):
- # TODO: Implement this
- pass
+ self.set_books_func = custom_series_index
elif field.is_many:
# TODO: Implement this
pass
From 364b5135a814fffa28cbafdf7f4298d4b05dc813 Mon Sep 17 00:00:00 2001
From: Ismael Mejia
Date: Fri, 22 Feb 2013 15:21:03 +0100
Subject: [PATCH 03/31] Added recipes for colombian media
---
recipes/el_malpensante.recipe | 27 +++++++++++++++++++++++++++
recipes/revista_cromos.recipe | 33 +++++++++++++++++++++++++++++++++
recipes/unperiodico.recipe | 22 ++++++++++++++++++++++
3 files changed, 82 insertions(+)
create mode 100644 recipes/el_malpensante.recipe
create mode 100644 recipes/revista_cromos.recipe
create mode 100644 recipes/unperiodico.recipe
diff --git a/recipes/el_malpensante.recipe b/recipes/el_malpensante.recipe
new file mode 100644
index 0000000000..7a014735b6
--- /dev/null
+++ b/recipes/el_malpensante.recipe
@@ -0,0 +1,27 @@
+# coding=utf-8
+# https://github.com/iemejia/calibrecolombia
+
+'''
+http://www.elmalpensante.com/
+'''
+
+from calibre.web.feeds.news import BasicNewsRecipe
+
+class ElMalpensante(BasicNewsRecipe):
+ title = u'El Malpensante'
+ language = 'es_CO'
+ __author__ = 'Ismael Mejia '
+ cover_url = 'http://elmalpensante.com/img/layout/logo.gif'
+ description = 'El Malpensante'
+ oldest_article = 30
+ simultaneous_downloads = 20
+ #tags = 'news, sport, blog'
+ use_embedded_content = True
+ remove_empty_feeds = True
+ max_articles_per_feed = 100
+ feeds = [(u'Artículos', u'http://www.elmalpensante.com/articulosRSS.php'),
+ (u'Malpensantías', u'http://www.elmalpensante.com/malpensantiasRSS.php'),
+ (u'Margaritas', u'http://www.elmalpensante.com/margaritasRSS.php'),
+# This one is almost the same as articulos so we leave articles
+# (u'Noticias', u'http://www.elmalpensante.com/noticiasRSS.php'),
+ ]
diff --git a/recipes/revista_cromos.recipe b/recipes/revista_cromos.recipe
new file mode 100644
index 0000000000..29515971dd
--- /dev/null
+++ b/recipes/revista_cromos.recipe
@@ -0,0 +1,33 @@
+# coding=utf-8
+# https://github.com/iemejia/calibrecolombia
+
+'''
+http://www.cromos.com.co/
+'''
+
+from calibre.web.feeds.news import BasicNewsRecipe
+
+class ElMalpensante(BasicNewsRecipe):
+ title = u'Revista Cromos'
+ language = 'es_CO'
+ __author__ = 'Ismael Mejia '
+ cover_url = 'http://www.cromos.com.co/sites/cromos.com.co/themes/cromos_theme/images/logo_morado.gif'
+ description = 'Revista Cromos'
+ oldest_article = 7
+ simultaneous_downloads = 20
+ #tags = 'news, sport, blog'
+ use_embedded_content = True
+ remove_empty_feeds = True
+ max_articles_per_feed = 100
+ feeds = [(u'Cromos', u'http://www.cromos.com.co/rss.xml'),
+ (u'Moda', u'http://www.cromos.com.co/moda/feed'),
+ (u'Estilo de Vida', u'http://www.cromos.com.co/estilo-de-vida/feed'),
+ (u'Cuidado Personal', u'http://www.cromos.com.co/estilo-de-vida/cuidado-personal/feed'),
+ (u'Salud y Alimentación', u'http://www.cromos.com.co/estilo-de-vida/salud-y-alimentacion/feed'),
+ (u'Personajes', u'http://www.cromos.com.co/personajes/feed'),
+ (u'Actualidad', u'http://www.cromos.com.co/personajes/actualidad/feed'),
+ (u'Espectáculo', u'http://www.cromos.com.co/personajes/espectaculo/feed'),
+ (u'Reportajes', u'http://www.cromos.com.co/reportajes/feed'),
+ (u'Eventos', u'http://www.cromos.com.co/eventos/feed'),
+ (u'Modelos', u'http://www.cromos.com.co/modelos/feed'),
+ ]
diff --git a/recipes/unperiodico.recipe b/recipes/unperiodico.recipe
new file mode 100644
index 0000000000..d4edb4e5dc
--- /dev/null
+++ b/recipes/unperiodico.recipe
@@ -0,0 +1,22 @@
+# -*- coding: utf-8 -*-
+# https://github.com/iemejia/calibrecolombia
+
+'''
+http://www.unperiodico.unal.edu.co/
+'''
+
+from calibre import strftime
+from calibre.web.feeds.news import BasicNewsRecipe
+
+class UNPeriodico(BasicNewsRecipe):
+ title = u'UN Periodico'
+ language = 'es_CO'
+ __author__ = 'Ismael Mejia '
+ cover_url = 'http://www.unperiodico.unal.edu.co/fileadmin/templates/periodico/img/logoperiodico.png'
+ description = 'UN Periodico'
+ oldest_article = 30
+ max_articles_per_feed = 100
+ publication_type = 'newspaper'
+ feeds = [
+ (u'UNPeriodico', u'http://www.unperiodico.unal.edu.co/rss/type/rss2/')
+ ]
From f2f8bcfae1346cce48e2ee2f37f0c2e58b3fd411 Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Fri, 22 Feb 2013 21:51:11 +0530
Subject: [PATCH 04/31] ...
---
src/calibre/gui2/main.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/calibre/gui2/main.py b/src/calibre/gui2/main.py
index f722cf226f..d43e618e9a 100644
--- a/src/calibre/gui2/main.py
+++ b/src/calibre/gui2/main.py
@@ -369,7 +369,7 @@ def build_pipe(print_error=True):
t.start()
t.join(3.0)
if t.is_alive():
- if iswindows():
+ if iswindows:
cant_start()
else:
f = os.path.expanduser('~/.calibre_calibre GUI.lock')
From d95b50d2e006c0de5eb9caec6515388d0d7453af Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Fri, 22 Feb 2013 22:08:09 +0530
Subject: [PATCH 05/31] Update wsj recipe for javascript login
---
recipes/wsj.recipe | 20 +++++++-------------
1 file changed, 7 insertions(+), 13 deletions(-)
diff --git a/recipes/wsj.recipe b/recipes/wsj.recipe
index f4254ee7cc..a6a7aa634d 100644
--- a/recipes/wsj.recipe
+++ b/recipes/wsj.recipe
@@ -55,20 +55,14 @@ class WallStreetJournal(BasicNewsRecipe):
]
remove_tags_after = [dict(id="article_story_body"), {'class':"article story"},]
+ use_javascript_to_login = True
- def get_browser(self):
- br = BasicNewsRecipe.get_browser(self)
- if self.username is not None and self.password is not None:
- br.open('http://commerce.wsj.com/auth/login')
- br.select_form(nr=1)
- br['user'] = self.username
- br['password'] = self.password
- res = br.submit()
- raw = res.read()
- if 'Welcome,' not in raw and '>Logout<' not in raw and '>Log Out<' not in raw:
- raise ValueError('Failed to log in to wsj.com, check your '
- 'username and password')
- return br
+ def javascript_login(self, br, username, password):
+ br.visit('https://id.wsj.com/access/pages/wsj/us/login_standalone.html?mg=com-wsj', timeout=120)
+ f = br.select_form(nr=0)
+ f['username'] = username
+ f['password'] = password
+ br.submit(timeout=120)
def populate_article_metadata(self, article, soup, first):
if first and hasattr(self, 'add_toc_thumbnail'):
From 1c874edd6bcab8a3e4dfc243982a671488e1215e Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Sat, 23 Feb 2013 10:16:25 +0530
Subject: [PATCH 06/31] E-book viewer: Fix bug in rendering prefixed svg tags
in the cover pages of some EPUB files.
---
src/calibre/ebooks/oeb/display/webview.py | 4 ++--
src/calibre/gui2/viewer/documentview.py | 3 +--
2 files changed, 3 insertions(+), 4 deletions(-)
diff --git a/src/calibre/ebooks/oeb/display/webview.py b/src/calibre/ebooks/oeb/display/webview.py
index a186044fe5..87a27d73e1 100644
--- a/src/calibre/ebooks/oeb/display/webview.py
+++ b/src/calibre/ebooks/oeb/display/webview.py
@@ -31,7 +31,7 @@ def self_closing_sub(match):
return '<%s%s>%s>'%(match.group(1), match.group(2), match.group(1))
def load_html(path, view, codec='utf-8', mime_type=None,
- pre_load_callback=lambda x:None, path_is_html=False,
+ pre_load_callback=lambda x:None, path_is_html=False,
force_as_html=False):
from PyQt4.Qt import QUrl, QByteArray
if mime_type is None:
@@ -45,7 +45,7 @@ def load_html(path, view, codec='utf-8', mime_type=None,
html = f.read().decode(codec, 'replace')
html = EntityDeclarationProcessor(html).processed_html
- self_closing_pat = re.compile(r'<\s*([A-Za-z1-6]+)([^>]*)/\s*>')
+ self_closing_pat = re.compile(r'<\s*([:A-Za-z1-6]+)([^>]*)/\s*>')
html = self_closing_pat.sub(self_closing_sub, html)
loading_url = QUrl.fromLocalFile(path)
diff --git a/src/calibre/gui2/viewer/documentview.py b/src/calibre/gui2/viewer/documentview.py
index 7df171a404..1878b5e760 100644
--- a/src/calibre/gui2/viewer/documentview.py
+++ b/src/calibre/gui2/viewer/documentview.py
@@ -790,8 +790,7 @@ class DocumentView(QWebView): # {{{
self.manager.load_started()
load_html(path, self, codec=getattr(path, 'encoding', 'utf-8'), mime_type=getattr(path,
- 'mime_type', 'text/html'), pre_load_callback=callback,
- force_as_html=True)
+ 'mime_type', 'text/html'), pre_load_callback=callback)
entries = set()
for ie in getattr(path, 'index_entries', []):
if ie.start_anchor:
From e708f0f183e087fb4dd06aeb6e7423b731d1cbf2 Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Sat, 23 Feb 2013 10:33:26 +0530
Subject: [PATCH 07/31] ...
---
src/calibre/ebooks/oeb/display/webview.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/calibre/ebooks/oeb/display/webview.py b/src/calibre/ebooks/oeb/display/webview.py
index 87a27d73e1..d7769cb33a 100644
--- a/src/calibre/ebooks/oeb/display/webview.py
+++ b/src/calibre/ebooks/oeb/display/webview.py
@@ -45,13 +45,13 @@ def load_html(path, view, codec='utf-8', mime_type=None,
html = f.read().decode(codec, 'replace')
html = EntityDeclarationProcessor(html).processed_html
- self_closing_pat = re.compile(r'<\s*([:A-Za-z1-6]+)([^>]*)/\s*>')
+ self_closing_pat = re.compile(r'<\s*([:A-Za-z0-9-]+)([^>]*)/\s*>')
html = self_closing_pat.sub(self_closing_sub, html)
loading_url = QUrl.fromLocalFile(path)
pre_load_callback(loading_url)
- if force_as_html or re.search(r'<[:a-zA-Z]*svg', html) is None:
+ if force_as_html or re.search(r'<[:a-zA-Z0-9-]*svg', html) is None:
view.setHtml(html, loading_url)
else:
view.setContent(QByteArray(html.encode(codec)), mime_type,
From d10a1a0b9019d193e1d8047224e82bc0cb1e73de Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Sat, 23 Feb 2013 11:15:49 +0530
Subject: [PATCH 08/31] Conversion: Do not rescale fonts sizes/adjust line
heights for text based drop caps defined using a separate tag (note
that drop caps defined using :first-letter were already handled correctly)
---
src/calibre/ebooks/oeb/transforms/flatcss.py | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/src/calibre/ebooks/oeb/transforms/flatcss.py b/src/calibre/ebooks/oeb/transforms/flatcss.py
index 03410e1a65..f2a0b1f2ea 100644
--- a/src/calibre/ebooks/oeb/transforms/flatcss.py
+++ b/src/calibre/ebooks/oeb/transforms/flatcss.py
@@ -363,7 +363,10 @@ class CSSFlattener(object):
cssdict['font-weight'] = 'normal' # ADE chokes on font-weight medium
fsize = font_size
- if not self.context.disable_font_rescaling:
+ is_drop_cap = (cssdict.get('float', None) == 'left' and 'font-size' in
+ cssdict and len(node) == 0 and node.text and
+ len(node.text) == 1)
+ if not self.context.disable_font_rescaling and not is_drop_cap:
_sbase = self.sbase if self.sbase is not None else \
self.context.source.fbase
dyn_rescale = dynamic_rescale_factor(node)
@@ -382,7 +385,7 @@ class CSSFlattener(object):
try:
minlh = self.context.minimum_line_height / 100.
- if style['line-height'] < minlh * fsize:
+ if not is_drop_cap and style['line-height'] < minlh * fsize:
cssdict['line-height'] = str(minlh)
except:
self.oeb.logger.exception('Failed to set minimum line-height')
From b442ea7df09a4a3348bf0542b78efef9d86b48d9 Mon Sep 17 00:00:00 2001
From: lacike
Date: Sat, 23 Feb 2013 07:35:33 +0100
Subject: [PATCH 09/31] hnonline icon is squarish now 50x50px
---
recipes/icons/hnonline.png | Bin 2732 -> 7639 bytes
1 file changed, 0 insertions(+), 0 deletions(-)
diff --git a/recipes/icons/hnonline.png b/recipes/icons/hnonline.png
index 561b2563cdf47b9f2484459c0c32ee1b57b0e792..1e073839ad3241d7879a60a7eccd5693769ce1fa 100644
GIT binary patch
literal 7639
zcmeAS@N?(olHy`uVBq!ia0vp^Mj*_=1SBWM%0B~AoCO|{#S9GG!XV7ZFl&wk1A}a*
zr;B4q#Vy7<*~UkV42CHT{}~wm|7T!i1Zo&C0JI#*8B8D!5DcuFfDS-)1}J0)7XZiE
z;Byu@*an}oMnj9_)I0bh5$uIgXOR+s=TrIR9+`s?+UAXc1#oLc?SvW&dl;1#2
z4rm=vF)~&qdmE_BVwJ@zNDpTLEtVD&Xv*^Eq8S{WnqCAT4++3+=FLtsmY5=itq!a
zn3))hA4>v8FyeLAzyJR~ef@!kzWw+`
zz))1_&tHF(_OJQ+9jc3+jiouuUq(z2RTVl9U)p;4_LF5?JDLtFePv*E#tf?y(PXi5
zf$3?%rlVP54v-37T$ryY)(w9#hR<1l{{DOT9ML*}Sg9f_2I-e$bpbBHh8deI^puP>
zOw1s4WJC-x#v`fu^XKoBmHQ5!xrro(lL;*#fQb>39&sw9hQzy%UyJ)z
zzy0`?pkYvF0b7zlKu|U13NGJ%Qrf?opgFWHPBE|8RzZCFFTK4ftNaHCCIPee|8+Z0(Z^Td!Uh0GyrV87x%6D`|mF|Cp%CI
z*wTOY>I0e@Mh1fvG@-%3rFSF}>E}^r5gAZmWuu`r8d{`;04Zum)7EHckrD!=s2xpP
aq=Z)VN>1tTQ=iFz^mw}ZxvX(_`g8%^e{{R4h=>PzAFaQARU;qF*m;eA5Z<1fd
zMgRZ`GD$>1RCwC#olS__=o!cVQ$nGlp@#GnNO4YcDyyM^(zFn}?4g&*cuo=QVPx2l
z(jI0Eb15zL_)sWp*l~`_1neAAw3pi1rSuZ8_R>R1v6i0ZH0p+KPXP}B6&A|$px23`
zkzRe;`tbh;CS)Y*m7e$Y^M9ZBm%qTz@ZS`RKRE@*@*3P}C>HPo+vUwV%D;xtylSzfboYcufJ?6m{y
z^krfLshc-YyQ(bh`XsgZG=k(>(J-Th2+3JM^t(Sr_rbRGoQO%p;01tdP|<_#SE
z@-Aze5JD&*@##L;hPru!HBJa26p;9gKK`|ILYxpnNOaa}8E^giHFO_rqx0}n)_ipc
z$}?uuG=xw|Be9_KppSC(`5!Xjfa`=nvH-%@1zEC
z4Aexqi|1S7JbQ&Uh;+X`cB}auvc2~%uD^J`Ev{`X;W@nbWugQrSsVkpXoqj^J8AnP
z6p+#|-=$urR3wgpIshHOp65sFJr4ke0G`DBF-uc}2uHUMpd~5Wf}03&yaJOXNo8`3
zfaHw;j)aZ7;u&iJ&u9zV9;FlwnJv5GyfJ`B1$N4NKK7)5tO0AE&b@JK6Z+lTWf6}f
z08`;u>Jh%81DH4lMj~QJTmG?d*sYLLRRE)`%FUrSJr%Z--FqEC*D){^5lx&jY9a%1
z6k9@^GjR+|%4SbO0jWI4hxZDB;9|7zNXR<5)Ka)B0
zl)cxPr_3gRen^=$$A~o6UnIr=+6&LvL3nzUJxN_QARo3Gddds4X+yFCjgWFhAVsJD
z^f~snZ%Z@s)~+f=%ax@|daN@5O`jg`*fG!(JybQI_u3K;D=rp*U7uyTrYLiQ;9($D
zIOU=<6VGURpAkSmWH?ckT(=vyya+bk2Uj~0Mj)%9-5rFUbMV3Y(l-#Um_5m{X3%hn
zaPYFXXziqMtad<|GZB>pai=WJM7CU3(a@GeB8`CPqb`X)0?QLNZA!{(aV~^VKvslx
z`V#%8&qL1}^x8!k$_nav4#PIGiQ~gKjxL08*aksy+$wPsu_NwXi5p3^f|_Jlc|t@U
zNls~G@!*buhGSsh7&vnbTr7O1VmY>+)lzV=9C`7h(-l)dN??c2HS05@NpVmz6KCR02<
zyocFqC$G9u(>gCjgC$a$kbDUeiQ{E_y49P1aA^F(#K
zJ4EBwrgW~Opzlz(*lbBg?D%v}+VcokL?{h$o-U4e5{SpZrw#AY{N-nDccQyPSwpq}
zvd(y;kM`p5sG?z}Xc&t~;U<7ioJ`9i<%*wpG>e{Lm{35%WAVwfa68Wl$&p&n0Y!^9
z*)7ClLh-0JSJvGC?d}lj<_}Q6evPexteOte4@stR#+D}v88`;+z+D|?r+DYd_u!wl
zr9<(cfK-a8tFrRAwSd1jV)g=qdXy%c|H|+>>N$4K^)Sy^u!t@`^LNVF;{Kn-h(Hbk
z{=Nh5oUatj#tU}=K$H!JIXImlVW>yEhRWJ4bY~#f-T7j^BWxv99bzAd*kqWdO&u_Q
z7X_r6V+jvGg?0L}NVjB5j_DDv5tIsry};EHwDBxZ?h{;R&c+zqQO)&^cBtb-SSbAakdo=LovtG_j%?PJx=+#LKD18|LJq=
zG}NMGhLqizz}b9D%IhRtIK+bW{TA{%Oc3R)*S<9!VZ9&`ob=0)9T^!o`^RN
z74C4B3E)fdxA#OH*S+=b7#PTQaf>-J@AqcjdKaRlE7~Oo?-#K);{1#}6Yf2@^C*k=
zG;j=BdCO*q;4v$iD9tA}K;)fd{t{Np!lGy>ja$87|NQOze33={El&$G{Pd9uDH5mZ6}
zDKGZduZpxwy#r*{OY0YO`R;gK`X(9H<$jtki}%#eMWX?CNc2n$ryYsU0Kl1#kUdG*
z8S?(9S{%xvfK-yA>{1uQ4xJ1xHB_`ruCt71vXir~=
zf^=No3^-2F3GwR;aa9%tq@vx$?lAP-AuAmI*orW8;KvN^n*XgqtG6rpe30LVdpJzvQu6&NG?5(tEhLpev$)kYE7;
z&M&4Xv;-lf5^xBM5JCtAgb+dq1%wbnNC^bwoMu=${{!$}nt>2fH39iM&9HR-3E&%=
zfe=zP0eM6-ES-;N0zya?1>`RPegk)yg=Ehk0Q`w&AcRy=Kmg#60DcTNJLgw4OKScN
m;4`?J{C-Ds5JIZR{|5kea%n(fnR5{U0000
Date: Sat, 23 Feb 2013 13:57:19 +0530
Subject: [PATCH 10/31] ...
---
src/calibre/db/write.py | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/src/calibre/db/write.py b/src/calibre/db/write.py
index cb710d271f..98e58f8594 100644
--- a/src/calibre/db/write.py
+++ b/src/calibre/db/write.py
@@ -13,11 +13,13 @@ from datetime import datetime
from calibre.constants import preferred_encoding, ispy3
from calibre.utils.date import (parse_only_date, parse_date, UNDEFINED_DATE,
isoformat)
+if ispy3:
+ unicode = str
# Convert data into values suitable for the db {{{
-if ispy3:
- unicode = str
+def sqlite_datetime(x):
+ return isoformat(x, sep=' ') if isinstance(x, datetime) else x
def single_text(x):
if x is None:
@@ -110,9 +112,7 @@ def get_adapter(name, metadata):
return ans
# }}}
-def sqlite_datetime(x):
- return isoformat(x, sep=' ') if isinstance(x, datetime) else x
-
+# One-One fields {{{
def one_one_in_books(book_id_val_map, db, field, *args):
'Set a one-one field in the books table'
if book_id_val_map:
@@ -152,6 +152,7 @@ def custom_series_index(book_id_val_map, db, field, *args):
db.conn.executemany('UPDATE %s SET %s=? WHERE book=? AND value=?'%(
field.metadata['table'], field.metadata['column']), sequence)
return {s[0] for s in sequence}
+# }}}
def dummy(book_id_val_map, *args):
return set()
From b4603c4be1550478d2d49f54cd0f5afbd4642a6f Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Sat, 23 Feb 2013 13:59:46 +0530
Subject: [PATCH 11/31] Fix #1132040 (recipe for Die Zeit (subscription only):
navigational links damaged due to insertion of whitespace)
---
recipes/zeitde_sub.recipe | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/recipes/zeitde_sub.recipe b/recipes/zeitde_sub.recipe
index b22e9793ed..3efcb610a3 100644
--- a/recipes/zeitde_sub.recipe
+++ b/recipes/zeitde_sub.recipe
@@ -88,7 +88,7 @@ class ZeitEPUBAbo(BasicNewsRecipe):
(re.compile(u' \u00AB'), lambda match: u'\u00AB '), # before closing quotation
(re.compile(u'\u00BB '), lambda match: u' \u00BB'), # after opening quotation
# filtering for spaces in large numbers for better readability
- (re.compile(r'(?<=\d\d)(?=\d\d\d[ ,\.;\)<\?!-])'), lambda match: u'\u2008'), # end of the number with some character following
+ (re.compile(r'(?<=\d\d)(?=\d\d\d[ ,;\)<\?!-])'), lambda match: u'\u2008'), # end of the number with some character following
(re.compile(r'(?<=\d\d)(?=\d\d\d. )'), lambda match: u'\u2008'), # end of the number with full-stop following, then space is necessary (avoid file names)
(re.compile(u'(?<=\d)(?=\d\d\d\u2008)'), lambda match: u'\u2008'), # next level
(re.compile(u'(?<=\d)(?=\d\d\d\u2008)'), lambda match: u'\u2008'), # next level
From 2f9d3e1c9f0ee114d68514353a7d69216be75bbc Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Sat, 23 Feb 2013 14:36:13 +0530
Subject: [PATCH 12/31] When sorting the book list on text fields like title,
recognize numbers inside the text and sort them as number. So the text 'Book
2' will sort before 'Book 100'. If you prefer the old behavior, you can
restore it via Preferences->Tweaks. Fixes #1132025 (Incorrect sort when title
contains numbers)
---
resources/default_tweaks.py | 8 ++++++++
src/calibre/utils/icu.c | 20 ++++++++++++++++++++
src/calibre/utils/icu.py | 2 ++
3 files changed, 30 insertions(+)
diff --git a/resources/default_tweaks.py b/resources/default_tweaks.py
index 1aa820819c..6b351ed18c 100644
--- a/resources/default_tweaks.py
+++ b/resources/default_tweaks.py
@@ -517,3 +517,11 @@ default_tweak_format = None
# your library and your personal editing style.
preselect_first_completion = False
+#: Recognize numbers inside text when sorting
+# This means that when sorting on text fields like title the text "Book 2"
+# will sort before the text "Book 100". This is how humans usually expect
+# things to sort. If you prefer the computer sort (which is a little faster,
+# but will cause Book 100 to sort before Book 2), then set numeric_collation
+# to False.
+numeric_collation = True
+
diff --git a/src/calibre/utils/icu.c b/src/calibre/utils/icu.c
index 3c133418b1..f1e8fce299 100644
--- a/src/calibre/utils/icu.c
+++ b/src/calibre/utils/icu.c
@@ -59,6 +59,7 @@ icu_Collator_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
PyErr_SetString(PyExc_Exception, "Failed to create collator.");
return NULL;
}
+ ucol_setAttribute(collator, UCOL_NUMERIC_COLLATION, UCOL_ON, &status);
self = (icu_Collator *)type->tp_alloc(type, 0);
if (self != NULL) {
@@ -110,6 +111,21 @@ icu_Collator_set_strength(icu_Collator *self, PyObject *val, void *closure) {
}
// }}}
+// Collator.numeric {{{
+static PyObject *
+icu_Collator_get_numeric(icu_Collator *self, void *closure) {
+ UErrorCode status = U_ZERO_ERROR;
+ return Py_BuildValue("O", (ucol_getAttribute(self->collator, UCOL_NUMERIC_COLLATION, &status) == UCOL_ON) ? Py_True : Py_False);
+}
+
+static int
+icu_Collator_set_numeric(icu_Collator *self, PyObject *val, void *closure) {
+ UErrorCode status = U_ZERO_ERROR;
+ ucol_setAttribute(self->collator, UCOL_NUMERIC_COLLATION, (PyObject_IsTrue(val)) ? UCOL_ON : UCOL_OFF, &status);
+ return 0;
+}
+// }}}
+
// Collator.actual_locale {{{
static PyObject *
icu_Collator_actual_locale(icu_Collator *self, void *closure) {
@@ -415,6 +431,10 @@ static PyGetSetDef icu_Collator_getsetters[] = {
(char *)"The strength of this collator.",
NULL},
+ {(char *)"numeric",
+ (getter)icu_Collator_get_numeric, (setter)icu_Collator_set_numeric,
+ (char *)"If True the collator sorts contiguous digits as numbers rather than strings, so 2 will sort before 10.",
+ NULL},
{NULL} /* Sentinel */
};
diff --git a/src/calibre/utils/icu.py b/src/calibre/utils/icu.py
index 66ee8fd59f..aa08f293fa 100644
--- a/src/calibre/utils/icu.py
+++ b/src/calibre/utils/icu.py
@@ -46,6 +46,8 @@ def load_collator():
icu = load_icu()
if icu is not None:
_collator = icu.Collator(get_locale())
+ if not tweaks['numeric_collation']:
+ _collator.numeric = False
return _collator
def primary_collator():
From 79231df21ce121eb101d002ae4c346cb278b737c Mon Sep 17 00:00:00 2001
From: Charles Haley <>
Date: Sat, 23 Feb 2013 10:11:59 +0100
Subject: [PATCH 13/31] Make bulk edit of custom columns respect the "apply
changes" box even if the value to set has not changed.
---
src/calibre/gui2/custom_column_widgets.py | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/src/calibre/gui2/custom_column_widgets.py b/src/calibre/gui2/custom_column_widgets.py
index 0f5bb0a1c9..3597b0fb19 100644
--- a/src/calibre/gui2/custom_column_widgets.py
+++ b/src/calibre/gui2/custom_column_widgets.py
@@ -622,8 +622,7 @@ class BulkBase(Base):
return
val = self.gui_val
val = self.normalize_ui_val(val)
- if val != self.initial_val:
- self.db.set_custom_bulk(book_ids, val, num=self.col_id, notify=notify)
+ self.db.set_custom_bulk(book_ids, val, num=self.col_id, notify=notify)
def make_widgets(self, parent, main_widget_class, extra_label_text=''):
w = QWidget(parent)
@@ -1030,8 +1029,7 @@ class BulkText(BulkBase):
else:
val = self.gui_val
val = self.normalize_ui_val(val)
- if val != self.initial_val:
- self.db.set_custom_bulk(book_ids, val, num=self.col_id, notify=notify)
+ self.db.set_custom_bulk(book_ids, val, num=self.col_id, notify=notify)
def getter(self):
if self.col_metadata['is_multiple']:
From b35fceb2df0114a76605dae4c599acd9f3245fad Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Sat, 23 Feb 2013 16:30:28 +0530
Subject: [PATCH 14/31] Only use numeric collation for sorting since it breaks
searching
---
src/calibre/utils/icu.c | 1 -
src/calibre/utils/icu.py | 34 ++++++++++++++++++++--------------
2 files changed, 20 insertions(+), 15 deletions(-)
diff --git a/src/calibre/utils/icu.c b/src/calibre/utils/icu.c
index f1e8fce299..ccb1cfb5b9 100644
--- a/src/calibre/utils/icu.c
+++ b/src/calibre/utils/icu.c
@@ -59,7 +59,6 @@ icu_Collator_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
PyErr_SetString(PyExc_Exception, "Failed to create collator.");
return NULL;
}
- ucol_setAttribute(collator, UCOL_NUMERIC_COLLATION, UCOL_ON, &status);
self = (icu_Collator *)type->tp_alloc(type, 0);
if (self != NULL) {
diff --git a/src/calibre/utils/icu.py b/src/calibre/utils/icu.py
index aa08f293fa..aaf8c415e1 100644
--- a/src/calibre/utils/icu.py
+++ b/src/calibre/utils/icu.py
@@ -12,7 +12,7 @@ from functools import partial
from calibre.constants import plugins
from calibre.utils.config_base import tweaks
-_icu = _collator = _primary_collator = _secondary_collator = None
+_icu = _collator = _primary_collator = _sort_collator = None
_locale = None
_none = u''
@@ -41,28 +41,34 @@ def load_icu():
return _icu
def load_collator():
+ 'The default collator for most locales takes both case and accented letters into account'
global _collator
if _collator is None:
icu = load_icu()
if icu is not None:
_collator = icu.Collator(get_locale())
- if not tweaks['numeric_collation']:
- _collator.numeric = False
return _collator
def primary_collator():
+ 'Ignores case differences and accented characters'
global _primary_collator
if _primary_collator is None:
_primary_collator = _collator.clone()
_primary_collator.strength = _icu.UCOL_PRIMARY
return _primary_collator
-def secondary_collator():
- global _secondary_collator
- if _secondary_collator is None:
- _secondary_collator = _collator.clone()
- _secondary_collator.strength = _icu.UCOL_SECONDARY
- return _secondary_collator
+def sort_collator():
+ 'Ignores case differences and recognizes numbers in strings'
+ global _sort_collator
+ if _sort_collator is None:
+ _sort_collator = _collator.clone()
+ _sort_collator.strength = _icu.UCOL_SECONDARY
+ if tweaks['numeric_collation']:
+ try:
+ _sort_collator.numeric = True
+ except AttributeError:
+ pass
+ return _sort_collator
def py_sort_key(obj):
if not obj:
@@ -74,15 +80,15 @@ def icu_sort_key(collator, obj):
return _none2
try:
try:
- return _secondary_collator.sort_key(obj)
+ return _sort_collator.sort_key(obj)
except AttributeError:
- return secondary_collator().sort_key(obj)
+ return sort_collator().sort_key(obj)
except TypeError:
if isinstance(obj, unicode):
obj = obj.replace(u'\0', u'')
else:
obj = obj.replace(b'\0', b'')
- return _secondary_collator.sort_key(obj)
+ return _sort_collator.sort_key(obj)
def icu_change_case(upper, locale, obj):
func = _icu.upper if upper else _icu.lower
@@ -235,9 +241,9 @@ def collation_order(a):
if _icu_not_ok:
return (ord(a[0]), 1) if a else (0, 0)
try:
- return icu_collation_order(_secondary_collator, a)
+ return icu_collation_order(_sort_collator, a)
except AttributeError:
- return icu_collation_order(secondary_collator(), a)
+ return icu_collation_order(sort_collator(), a)
################################################################################
From 67cbdb9ad1c8d43fb3cbe87c9c9076e17ad8161d Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Sat, 23 Feb 2013 17:03:27 +0530
Subject: [PATCH 15/31] Replace use of strcmp() for sorting with sort_key()
---
src/calibre/db/backend.py | 6 ++++--
src/calibre/devices/kobo/books.py | 7 +++----
src/calibre/devices/usbms/books.py | 7 +++----
src/calibre/library/sqlite.py | 5 +++--
4 files changed, 13 insertions(+), 12 deletions(-)
diff --git a/src/calibre/db/backend.py b/src/calibre/db/backend.py
index c70c656f78..61ee2e3a18 100644
--- a/src/calibre/db/backend.py
+++ b/src/calibre/db/backend.py
@@ -20,7 +20,7 @@ from calibre.ptempfile import PersistentTemporaryFile
from calibre.db.schema_upgrades import SchemaUpgrade
from calibre.library.field_metadata import FieldMetadata
from calibre.ebooks.metadata import title_sort, author_to_author_sort
-from calibre.utils.icu import strcmp
+from calibre.utils.icu import sort_key
from calibre.utils.config import to_json, from_json, prefs, tweaks
from calibre.utils.date import utcfromtimestamp, parse_date
from calibre.utils.filenames import (is_case_sensitive, samefile, hardlink_file)
@@ -172,7 +172,9 @@ def _author_to_author_sort(x):
return author_to_author_sort(x.replace('|', ','))
def icu_collator(s1, s2):
- return strcmp(force_unicode(s1, 'utf-8'), force_unicode(s2, 'utf-8'))
+ return cmp(sort_key(force_unicode(s1, 'utf-8')),
+ sort_key(force_unicode(s2, 'utf-8')))
+
# }}}
# Unused aggregators {{{
diff --git a/src/calibre/devices/kobo/books.py b/src/calibre/devices/kobo/books.py
index fc18a61fc8..fb502bccac 100644
--- a/src/calibre/devices/kobo/books.py
+++ b/src/calibre/devices/kobo/books.py
@@ -6,7 +6,7 @@ import os, time, sys
from calibre.constants import preferred_encoding, DEBUG
from calibre import isbytestring, force_unicode
-from calibre.utils.icu import strcmp
+from calibre.utils.icu import sort_key
from calibre.devices.usbms.books import Book as Book_
from calibre.devices.usbms.books import CollectionsBookList
@@ -239,9 +239,8 @@ class KTCollectionsBookList(CollectionsBookList):
if y is None:
return -1
if isinstance(x, basestring) and isinstance(y, basestring):
- c = strcmp(force_unicode(x), force_unicode(y))
- else:
- c = cmp(x, y)
+ x, y = sort_key(force_unicode(x)), sort_key(force_unicode(y))
+ c = cmp(x, y)
if c != 0:
return c
# same as above -- no sort_key needed here
diff --git a/src/calibre/devices/usbms/books.py b/src/calibre/devices/usbms/books.py
index 7d6377ca96..97527a92ed 100644
--- a/src/calibre/devices/usbms/books.py
+++ b/src/calibre/devices/usbms/books.py
@@ -13,7 +13,7 @@ from calibre.devices.interface import BookList as _BookList
from calibre.constants import preferred_encoding
from calibre import isbytestring, force_unicode
from calibre.utils.config import device_prefs, tweaks
-from calibre.utils.icu import strcmp
+from calibre.utils.icu import sort_key
from calibre.utils.formatter import EvalFormatter
class Book(Metadata):
@@ -281,9 +281,8 @@ class CollectionsBookList(BookList):
if y is None:
return -1
if isinstance(x, basestring) and isinstance(y, basestring):
- c = strcmp(force_unicode(x), force_unicode(y))
- else:
- c = cmp(x, y)
+ x, y = sort_key(force_unicode(x)), sort_key(force_unicode(y))
+ c = cmp(x, y)
if c != 0:
return c
# same as above -- no sort_key needed here
diff --git a/src/calibre/library/sqlite.py b/src/calibre/library/sqlite.py
index 90d293ba64..2e438ceb55 100644
--- a/src/calibre/library/sqlite.py
+++ b/src/calibre/library/sqlite.py
@@ -20,7 +20,7 @@ from calibre.ebooks.metadata import title_sort, author_to_author_sort
from calibre.utils.date import parse_date, isoformat, local_tz, UNDEFINED_DATE
from calibre import isbytestring, force_unicode
from calibre.constants import iswindows, DEBUG, plugins
-from calibre.utils.icu import strcmp
+from calibre.utils.icu import sort_key
from calibre import prints
from dateutil.tz import tzoffset
@@ -189,7 +189,8 @@ def pynocase(one, two, encoding='utf-8'):
return cmp(one.lower(), two.lower())
def icu_collator(s1, s2):
- return strcmp(force_unicode(s1, 'utf-8'), force_unicode(s2, 'utf-8'))
+ return cmp(sort_key(force_unicode(s1, 'utf-8')),
+ sort_key(force_unicode(s2, 'utf-8')))
def load_c_extensions(conn, debug=DEBUG):
try:
From 626993f6bc1b92a620bf3256af85f536fbbd5975 Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Sat, 23 Feb 2013 21:57:32 +0530
Subject: [PATCH 16/31] Polishing books: Ignore unsupported fonts instead of
erroring out on them. Fixes #1132085 (Private bug)
---
src/calibre/ebooks/oeb/polish/subset.py | 14 +++++++++++---
1 file changed, 11 insertions(+), 3 deletions(-)
diff --git a/src/calibre/ebooks/oeb/polish/subset.py b/src/calibre/ebooks/oeb/polish/subset.py
index 191d5265a4..adee8b2fb6 100644
--- a/src/calibre/ebooks/oeb/polish/subset.py
+++ b/src/calibre/ebooks/oeb/polish/subset.py
@@ -9,10 +9,11 @@ __docformat__ = 'restructuredtext en'
import os, sys
-from calibre import prints
+from calibre import prints, as_unicode
from calibre.ebooks.oeb.base import OEB_STYLES, OEB_DOCS, XPath
from calibre.ebooks.oeb.polish.container import OEB_FONTS
from calibre.utils.fonts.sfnt.subset import subset
+from calibre.utils.fonts.sfnt.errors import UnsupportedFont
from calibre.utils.fonts.utils import get_font_names
def remove_font_face_rules(container, sheet, remove_names, base):
@@ -46,9 +47,16 @@ def subset_all_fonts(container, font_stats, report):
raw = f.read()
font_name = get_font_names(raw)[-1]
warnings = []
- container.log('Subsetting font: %s'%font_name)
- nraw, old_sizes, new_sizes = subset(raw, chars,
+ container.log('Subsetting font: %s'%(font_name or name))
+ try:
+ nraw, old_sizes, new_sizes = subset(raw, chars,
warnings=warnings)
+ except UnsupportedFont as e:
+ container.log.warning(
+ 'Unsupported font: %s, ignoring. Error: %s'%(
+ name, as_unicode(e)))
+ continue
+
for w in warnings:
container.log.warn(w)
olen = sum(old_sizes.itervalues())
From d464e8b70e65efb7139f2a5a60d734a7afe134a8 Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Sat, 23 Feb 2013 21:58:36 +0530
Subject: [PATCH 17/31] Nezavisne Novine by Darko Miletic. Fixes #1132143 (New
recipe for Nezavisne novine)
---
recipes/icons/nezavisne_novine.png | Bin 0 -> 454 bytes
recipes/nezavisne_novine.recipe | 59 +++++++++++++++++++++++++++++
src/calibre/utils/icu.py | 21 ++++++++++
3 files changed, 80 insertions(+)
create mode 100644 recipes/icons/nezavisne_novine.png
create mode 100644 recipes/nezavisne_novine.recipe
diff --git a/recipes/icons/nezavisne_novine.png b/recipes/icons/nezavisne_novine.png
new file mode 100644
index 0000000000000000000000000000000000000000..29da3de24f10c21ad2c38354d010e11a00c25ec8
GIT binary patch
literal 454
zcmV;%0XhDOP)g)XXy9Y}t(QknN5}-_QAz&zUyD-KeC>NY7
z$K!Tsmh&kPnp}&mp1`mYX=w`hKnzZ856>h(oR4=@lofppXzGBiqyV;!EafQw=zL$v
z3Lt?51HQav`{SEdz;S*EaE>^$IX1U@d6(9ZbR6`x5_=^G5MZ$v3r2!Nvy>3qLEk#G
zZ(xWLV7#{#&i%9N==6q%Wc{xxe*YjDgJML+kc8%28l8_V?7(0wM*3pQ6Z2c3gGO@V
zb`ONRsg5NMWa?46T%||r_6-!#91tTM4K?eSgKm7jN(%<`hPzE)xzdavcR_w7Vk#o(
z)jjcnc5l}38_5)-w5D3G3M3DYL5uoYP1mgeB^og`6;KE^RCbo22V&mPTuHbmb9#|{
weo{Kr<$Qw;HCV9zn{@iI-$h<4{QGNt2a(tVEjMS$O#lD@07*qoM6N<$f;vyeeE'
+'''
+www.nezavisne.com
+'''
+from calibre import strftime
+from calibre.web.feeds.news import BasicNewsRecipe
+
+class NezavisneNovine(BasicNewsRecipe):
+ title = 'Nezavisne novine'
+ __author__ = 'Darko Miletic'
+ description = 'Nezavisne novine - Najnovije vijesti iz BiH, Srbije, Hrvatske, Crne Gore i svijeta'
+ publisher = 'NIGP "DNN"'
+ category = 'news, politics, Bosnia, Balcans'
+ oldest_article = 2
+ max_articles_per_feed = 200
+ no_stylesheets = True
+ encoding = 'utf8'
+ use_embedded_content = False
+ language = 'sr'
+ remove_empty_feeds = True
+ publication_type = 'newspaper'
+ cover_url = strftime('http://pdf.nezavisne.com/slika/novina/nezavisne_novine.jpg?v=%Y%m%d')
+ masthead_url = 'http://www.nezavisne.com/slika/osnova/nezavisne-novine-logo.gif'
+ extra_css = """
+ body{font-family: Arial,Helvetica,sans-serif }
+ img{margin-bottom: 0.4em; display:block}
+ """
+
+ conversion_options = {
+ 'comment' : description
+ , 'tags' : category
+ , 'publisher' : publisher
+ , 'language' : language
+ }
+ keep_only_tags = [dict(name='div', attrs={'class':'vijest'})]
+ remove_tags_after = dict(name='div', attrs={'id':'wrap'})
+ remove_tags = [
+ dict(name=['meta','link','iframe','object'])
+ ,dict(name='div', attrs={'id':'wrap'})
+ ]
+ remove_attributes=['lang','xmlns:fb','xmlns:og']
+
+
+ feeds = [
+ (u'Novosti' , u'http://feeds.feedburner.com/Novosti-NezavisneNovine' )
+ ,(u'Posao' , u'http://feeds.feedburner.com/Posao-NezavisneNovine' )
+ ,(u'Sport' , u'http://feeds.feedburner.com/Sport-NezavisneNovine' )
+ ,(u'Komentar' , u'http://feeds.feedburner.com/Komentari-NezavisneNovine' )
+ ,(u'Umjetnost i zabava' , u'http://feeds.feedburner.com/UmjetnostIZabava-NezavisneNovine' )
+ ,(u'Život i stil' , u'http://feeds.feedburner.com/ZivotIStil-NezavisneNovine' )
+ ,(u'Auto' , u'http://feeds.feedburner.com/Auto-NezavisneNovine' )
+ ,(u'Nauka i tehnologija', u'http://feeds.feedburner.com/NaukaITehnologija-NezavisneNovine')
+ ]
+
+ def preprocess_html(self, soup):
+ for item in soup.findAll(style=True):
+ del item['style']
+ return soup
diff --git a/src/calibre/utils/icu.py b/src/calibre/utils/icu.py
index aaf8c415e1..e1e6c1a1c6 100644
--- a/src/calibre/utils/icu.py
+++ b/src/calibre/utils/icu.py
@@ -341,6 +341,7 @@ pêché'''
german = create(german)
c = _icu.Collator('de')
+ c.numeric = True
gs = list(sorted(german, key=c.sort_key))
if gs != create(german_good):
print 'German sorting failed'
@@ -348,6 +349,7 @@ pêché'''
print
french = create(french)
c = _icu.Collator('fr')
+ c.numeric = True
fs = list(sorted(french, key=c.sort_key))
if fs != create(french_good):
print 'French sorting failed (note that French fails with icu < 4.6)'
@@ -396,6 +398,25 @@ pêché'''
print 'startswith() failed'
return
+ print '\nTesting collation_order()'
+ for group in [
+ ('Šaa', 'Smith', 'Solženicyn', 'Štepánek'),
+ ('calibre', 'Charon', 'Collins'),
+ ('01', '1'),
+ ('1', '11', '13'),
+ ]:
+ last = None
+ for x in group:
+ val = icu_collation_order(sort_collator(), x)
+ if val[1] != 1:
+ prints('collation_order() returned incorrect length for', x)
+ if last is None:
+ last = val
+ else:
+ if val != last:
+ prints('collation_order() returned incorrect value for', x)
+ last = val
+
# }}}
if __name__ == '__main__':
From b76b53bea0799fc792096da62fffa35f7dda256e Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Sat, 23 Feb 2013 22:15:20 +0530
Subject: [PATCH 18/31] Turn off numeric colaltion by default
---
resources/default_tweaks.py | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/resources/default_tweaks.py b/resources/default_tweaks.py
index 6b351ed18c..9f6297ac79 100644
--- a/resources/default_tweaks.py
+++ b/resources/default_tweaks.py
@@ -519,9 +519,8 @@ preselect_first_completion = False
#: Recognize numbers inside text when sorting
# This means that when sorting on text fields like title the text "Book 2"
-# will sort before the text "Book 100". This is how humans usually expect
-# things to sort. If you prefer the computer sort (which is a little faster,
-# but will cause Book 100 to sort before Book 2), then set numeric_collation
-# to False.
-numeric_collation = True
+# will sort before the text "Book 100". If you want this behavior, set
+# numeric_collation = True note that doing so will cause problems with text
+# that starts with numbers and is a little slower.
+numeric_collation = False
From 68bbf644524e6a450529f569529c57c568e51a8f Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Sat, 23 Feb 2013 22:52:07 +0530
Subject: [PATCH 19/31] Book polishing: Make updating cover a separate option,
so you can now update metadata without updating the ocver.
---
src/calibre/ebooks/oeb/polish/main.py | 1 +
src/calibre/gui2/actions/polish.py | 24 +++++++++++++++---------
2 files changed, 16 insertions(+), 9 deletions(-)
diff --git a/src/calibre/ebooks/oeb/polish/main.py b/src/calibre/ebooks/oeb/polish/main.py
index bd222cdbd2..c04686ed6c 100644
--- a/src/calibre/ebooks/oeb/polish/main.py
+++ b/src/calibre/ebooks/oeb/polish/main.py
@@ -174,6 +174,7 @@ def gui_polish(data):
files = data.pop('files')
if not data.pop('metadata'):
data.pop('opf')
+ if not data.pop('do_cover'):
data.pop('cover')
file_map = {x:x for x in files}
opts = ALL_OPTS.copy()
diff --git a/src/calibre/gui2/actions/polish.py b/src/calibre/gui2/actions/polish.py
index aeb3a2e332..dd41c0690f 100644
--- a/src/calibre/gui2/actions/polish.py
+++ b/src/calibre/gui2/actions/polish.py
@@ -44,13 +44,18 @@ class Polish(QDialog): # {{{
_('Smarten punctuation
%s')%HELP['smarten_punctuation'],
'metadata':_('Updating metadata
'
- 'This will update all metadata and covers in the'
+ '
This will update all metadata except the cover in the'
' ebook files to match the current metadata in the'
- ' calibre library.
If the ebook file does not have'
- ' an identifiable cover, a new cover is inserted.
'
+ ' calibre library.
'
' Note that most ebook'
' formats are not capable of supporting all the'
- ' metadata in calibre.
'),
+ ' metadata in calibre.There is a separate option to'
+ ' update the cover.
'),
+ 'do_cover': _('Update the covers in the ebook files to match the'
+ ' current cover in the calibre library.
'
+ 'If the ebook file does not have'
+ ' an identifiable cover, a new cover is inserted.
'
+ ),
'jacket':_('Book Jacket
%s')%HELP['jacket'],
'remove_jacket':_('Remove Book Jacket
%s')%HELP['remove_jacket'],
}
@@ -63,11 +68,12 @@ class Polish(QDialog): # {{{
count = 0
self.all_actions = OrderedDict([
- ('subset', _('Subset all embedded fonts')),
- ('smarten_punctuation', _('Smarten punctuation')),
- ('metadata', _('Update metadata in book files')),
- ('jacket', _('Add metadata as a "book jacket" page')),
- ('remove_jacket', _('Remove a previously inserted book jacket')),
+ ('subset', _('&Subset all embedded fonts')),
+ ('smarten_punctuation', _('Smarten &punctuation')),
+ ('metadata', _('Update &metadata in the book files')),
+ ('do_cover', _('Update the &cover in the book files')),
+ ('jacket', _('Add metadata as a "book &jacket" page')),
+ ('remove_jacket', _('&Remove a previously inserted book jacket')),
])
prefs = gprefs.get('polishing_settings', {})
for name, text in self.all_actions.iteritems():
From 32a6b097efd2696ff42c4f165dbcd73a7d644afc Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Sat, 23 Feb 2013 23:02:38 +0530
Subject: [PATCH 20/31] Book polishing: Fix bug that caused the ORIGINAL_EPUB
format to be replaced by the EPUB format when polishing a book with both
ORIGINA_EPUB and EPUB
---
src/calibre/gui2/actions/polish.py | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/src/calibre/gui2/actions/polish.py b/src/calibre/gui2/actions/polish.py
index dd41c0690f..1be05f181b 100644
--- a/src/calibre/gui2/actions/polish.py
+++ b/src/calibre/gui2/actions/polish.py
@@ -249,8 +249,10 @@ class Polish(QDialog): # {{{
cover = os.path.join(base, 'cover.jpg')
if db.copy_cover_to(book_id, cover, index_is_id=True):
data['cover'] = cover
+ is_orig = {}
for fmt in formats:
ext = fmt.replace('ORIGINAL_', '').lower()
+ is_orig[ext.upper()] = 'ORIGINAL_' in fmt
with open(os.path.join(base, '%s.%s'%(book_id, ext)), 'wb') as f:
db.copy_format_to(book_id, fmt, f, index_is_id=True)
data['files'].append(f.name)
@@ -263,7 +265,7 @@ class Polish(QDialog): # {{{
self.pd.set_msg(_('Queueing book %(nums)s of %(tot)s (%(title)s)')%dict(
nums=num, tot=len(self.book_id_map), title=mi.title))
- self.jobs.append((desc, data, book_id, base))
+ self.jobs.append((desc, data, book_id, base, is_orig))
# }}}
class Report(QDialog): # {{{
@@ -410,11 +412,11 @@ class PolishAction(InterfaceAction):
d = Polish(self.gui.library_view.model().db, book_id_map, parent=self.gui)
if d.exec_() == d.Accepted and d.jobs:
show_reports = bool(d.show_reports.isChecked())
- for desc, data, book_id, base in reversed(d.jobs):
+ for desc, data, book_id, base, is_orig in reversed(d.jobs):
job = self.gui.job_manager.run_job(
Dispatcher(self.book_polished), 'gui_polish', args=(data,),
description=desc)
- job.polish_args = (book_id, base, data['files'], show_reports)
+ job.polish_args = (book_id, base, data['files'], show_reports, is_orig)
if d.jobs:
self.gui.jobs_pointer.start()
self.gui.status_bar.show_message(
@@ -425,11 +427,11 @@ class PolishAction(InterfaceAction):
self.gui.job_exception(job)
return
db = self.gui.current_db
- book_id, base, files, show_reports = job.polish_args
+ book_id, base, files, show_reports, is_orig = job.polish_args
fmts = set()
for path in files:
fmt = path.rpartition('.')[-1].upper()
- if tweaks['save_original_format_when_polishing']:
+ if tweaks['save_original_format_when_polishing'] and not is_orig[fmt]:
fmts.add(fmt)
db.save_original_format(book_id, fmt, notify=False)
with open(path, 'rb') as f:
From f2782c979f36347fde9a40abfb3793193e100b76 Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Sun, 24 Feb 2013 10:05:37 +0530
Subject: [PATCH 21/31] Fix #1132220 (Translation: word order + missing texts)
---
src/calibre/gui2/viewer/main.py | 8 +-
src/calibre/gui2/widgets.py | 4 +-
src/calibre/translations/calibre.pot | 235 ++++++++++++++-------------
3 files changed, 125 insertions(+), 122 deletions(-)
diff --git a/src/calibre/gui2/viewer/main.py b/src/calibre/gui2/viewer/main.py
index b1b0e7bd87..4587a6542b 100644
--- a/src/calibre/gui2/viewer/main.py
+++ b/src/calibre/gui2/viewer/main.py
@@ -725,13 +725,15 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
self.view.shrink_fonts()
def magnification_changed(self, val):
- tt = _('%(which)s font size [%(sc)s]\nCurrent magnification: %(mag).1f')
+ tt = '%(action)s [%(sc)s]\n'+_('Current magnification: %(mag).1f')
sc = unicode(self.action_font_size_larger.shortcut().toString())
self.action_font_size_larger.setToolTip(
- tt %dict(which=_('Increase'), mag=val, sc=sc))
+ tt %dict(action=unicode(self.action_font_size_larger.text()),
+ mag=val, sc=sc))
sc = unicode(self.action_font_size_smaller.shortcut().toString())
self.action_font_size_smaller.setToolTip(
- tt %dict(which=_('Decrease'), mag=val, sc=sc))
+ tt %dict(action=unicode(self.action_font_size_smaller.text()),
+ mag=val, sc=sc))
self.action_font_size_larger.setEnabled(self.view.multiplier < 3)
self.action_font_size_smaller.setEnabled(self.view.multiplier > 0.2)
diff --git a/src/calibre/gui2/widgets.py b/src/calibre/gui2/widgets.py
index 997502a9d7..5d3d1e26c6 100644
--- a/src/calibre/gui2/widgets.py
+++ b/src/calibre/gui2/widgets.py
@@ -955,8 +955,8 @@ class LayoutButton(QToolButton):
def set_state_to_hide(self, *args):
self.setChecked(True)
- label = _('Hide')
- self.setText(label + ' ' + self.label+ u' (%s)'%self.shortcut)
+ self.setText(_('Hide %(label)s %(shortcut)s'%dict(
+ label=self.label, shortcut=self.shortcut)))
self.setToolTip(self.text())
self.setStatusTip(self.text())
diff --git a/src/calibre/translations/calibre.pot b/src/calibre/translations/calibre.pot
index 52a0aa73d2..c56c518d1c 100644
--- a/src/calibre/translations/calibre.pot
+++ b/src/calibre/translations/calibre.pot
@@ -5,8 +5,8 @@
msgid ""
msgstr ""
"Project-Id-Version: calibre 0.9.20\n"
-"POT-Creation-Date: 2013-02-22 10:18+IST\n"
-"PO-Revision-Date: 2013-02-22 10:18+IST\n"
+"POT-Creation-Date: 2013-02-24 10:04+IST\n"
+"PO-Revision-Date: 2013-02-24 10:04+IST\n"
"Last-Translator: Automatically generated\n"
"Language-Team: LANGUAGE\n"
"MIME-Version: 1.0\n"
@@ -24,8 +24,8 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/db/cache.py:124
#: /home/kovid/work/calibre/src/calibre/db/cache.py:127
#: /home/kovid/work/calibre/src/calibre/db/cache.py:138
-#: /home/kovid/work/calibre/src/calibre/db/write.py:100
#: /home/kovid/work/calibre/src/calibre/db/write.py:102
+#: /home/kovid/work/calibre/src/calibre/db/write.py:106
#: /home/kovid/work/calibre/src/calibre/devices/android/driver.py:383
#: /home/kovid/work/calibre/src/calibre/devices/android/driver.py:384
#: /home/kovid/work/calibre/src/calibre/devices/hanvon/driver.py:114
@@ -882,8 +882,8 @@ msgstr ""
msgid "Disable the named plugin"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/db/backend.py:321
-#: /home/kovid/work/calibre/src/calibre/db/backend.py:330
+#: /home/kovid/work/calibre/src/calibre/db/backend.py:323
+#: /home/kovid/work/calibre/src/calibre/db/backend.py:332
#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:322
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/choose_library.py:98
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:749
@@ -896,7 +896,7 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/db/cache.py:152
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/book/base.py:666
#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:67
-#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:678
+#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:677
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1030
#: /home/kovid/work/calibre/src/calibre/utils/formatter_functions.py:887
#: /home/kovid/work/calibre/src/calibre/utils/formatter_functions.py:910
@@ -908,25 +908,25 @@ msgstr ""
msgid "%(tt)sAverage rating is %(rating)3.1f"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/db/fields.py:232
+#: /home/kovid/work/calibre/src/calibre/db/fields.py:233
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1187
msgid "Main"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/db/fields.py:234
+#: /home/kovid/work/calibre/src/calibre/db/fields.py:235
#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:77
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1189
msgid "Card A"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/db/fields.py:236
+#: /home/kovid/work/calibre/src/calibre/db/fields.py:237
#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:79
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1191
msgid "Card B"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/db/fields.py:471
-#: /home/kovid/work/calibre/src/calibre/db/fields.py:486
+#: /home/kovid/work/calibre/src/calibre/db/fields.py:472
+#: /home/kovid/work/calibre/src/calibre/db/fields.py:487
#: /home/kovid/work/calibre/src/calibre/devices/apple/driver.py:2822
#: /home/kovid/work/calibre/src/calibre/devices/nook/driver.py:106
#: /home/kovid/work/calibre/src/calibre/devices/prs505/sony_cache.py:448
@@ -3479,7 +3479,7 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/book/base.py:666
#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:67
-#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:678
+#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:677
msgid "No"
msgstr ""
@@ -3928,7 +3928,7 @@ msgid ""
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/polish/main.py:48
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:392
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:400
#: /home/kovid/work/calibre/src/calibre/gui2/preferences/look_feel.py:197
#: /home/kovid/work/calibre/src/calibre/gui2/shortcuts.py:132
#: /home/kovid/work/calibre/src/calibre/gui2/shortcuts.py:223
@@ -4034,27 +4034,27 @@ msgstr ""
msgid "Polishing took: %.1f seconds"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/polish/main.py:201
+#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/polish/main.py:202
msgid "Path to a cover image. Changes the cover specified in the ebook. If no cover is present, or the cover is not properly identified, inserts a new cover."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/polish/main.py:204
+#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/polish/main.py:205
msgid "Path to an OPF file. The metadata in the book is updated from the OPF file."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/polish/main.py:209
+#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/polish/main.py:210
msgid "Produce more verbose output, useful for debugging."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/polish/main.py:219
+#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/polish/main.py:220
msgid "You must provide the input file to polish"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/polish/main.py:223
+#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/polish/main.py:224
msgid "Unknown extra arguments"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/polish/main.py:241
+#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/polish/main.py:242
msgid "You must specify at least one action to perform"
msgstr ""
@@ -4385,7 +4385,7 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/actions/edit_metadata.py:192
#: /home/kovid/work/calibre/src/calibre/gui2/actions/edit_metadata.py:256
#: /home/kovid/work/calibre/src/calibre/gui2/actions/edit_metadata.py:293
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:377
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:385
#: /home/kovid/work/calibre/src/calibre/gui2/actions/save_to_disk.py:82
#: /home/kovid/work/calibre/src/calibre/gui2/actions/view.py:271
msgid "No books selected"
@@ -5393,165 +5393,175 @@ msgid "Smarten punctuation
%s"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:46
-msgid "Updating metadata
This will update all metadata and covers in the ebook files to match the current metadata in the calibre library.
If the ebook file does not have an identifiable cover, a new cover is inserted.
Note that most ebook formats are not capable of supporting all the metadata in calibre.
"
+msgid "Updating metadata
This will update all metadata except the cover in the ebook files to match the current metadata in the calibre library.
Note that most ebook formats are not capable of supporting all the metadata in calibre.
There is a separate option to update the cover.
"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:54
+msgid "Update the covers in the ebook files to match the current cover in the calibre library.
If the ebook file does not have an identifiable cover, a new cover is inserted.
"
+msgstr ""
+
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:59
#, python-format
msgid "Book Jacket
%s"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:55
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:60
#, python-format
msgid "Remove Book Jacket
%s"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:61
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:66
msgid "Select actions to perform:"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:66
-msgid "Subset all embedded fonts"
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:71
+#: /home/kovid/work/calibre/src/calibre/gui2/convert/look_and_feel_ui.py:249
+msgid "&Subset all embedded fonts"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:67
-msgid "Smarten punctuation"
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:72
+#: /home/kovid/work/calibre/src/calibre/gui2/convert/look_and_feel_ui.py:240
+msgid "Smarten &punctuation"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:68
-msgid "Update metadata in book files"
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:73
+msgid "Update &metadata in the book files"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:69
-msgid "Add metadata as a \"book jacket\" page"
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:74
+msgid "Update the &cover in the book files"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:70
-msgid "Remove a previously inserted book jacket"
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:75
+msgid "Add metadata as a \"book &jacket\" page"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:80
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:76
+msgid "&Remove a previously inserted book jacket"
+msgstr ""
+
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:86
msgid "About"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:99
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:105
msgid "Show &report"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:101
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:107
msgid "Show a report of all the actions performed after polishing is completed"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:107
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:113
msgid "&Save Settings"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:109
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:115
msgid "&Load Settings"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:112
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:118
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/duplicates.py:47
msgid "Select &all"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:114
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:120
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/duplicates.py:49
msgid "Select &none"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:130
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:195
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:136
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:201
msgid "No actions selected"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:131
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:137
msgid "You must select at least one action before saving"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:133
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:139
msgid "Choose name"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:134
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:140
msgid "Choose a name for these settings"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:154
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:160
msgid "Remove saved settings"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:196
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:202
msgid "You must select at least one action, or click Cancel."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:210
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:216
msgid "Queueing books for polishing"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:252
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:260
#, python-format
msgid "Polish %s"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:253
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:261
#, python-format
msgid "Polish book %(nums)s of %(tot)s (%(title)s)"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:257
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:265
#, python-format
msgid "Queueing book %(nums)s of %(tot)s (%(title)s)"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:283
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:291
#, python-format
msgid "Ignore remaining %d reports"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:290
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:298
msgid "View full &log"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:313
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:321
#, python-format
msgid "Polishing of %s"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:319
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:327
#, python-format
msgid "The original file has been saved as %s."
msgid_plural "The original files have been saved as %s."
msgstr[0] ""
msgstr[1] ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:321
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:329
msgid " and "
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:324
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:332
msgid "If you polish again, the polishing will run on the originals."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:359
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:367
msgid "P"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:359
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:367
msgid "Polish books"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:376
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:389
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:384
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:397
msgid "Cannot polish"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:390
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:398
#, python-format
msgid "Polishing is only supported for books in the %s formats. Convert to one of those formats before polishing."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:415
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/polish.py:423
#, python-format
msgid "Start polishing of %d book(s)"
msgstr ""
@@ -7317,10 +7327,6 @@ msgstr ""
msgid "Text &justification:"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/convert/look_and_feel_ui.py:240
-msgid "Smarten &punctuation"
-msgstr ""
-
#: /home/kovid/work/calibre/src/calibre/gui2/convert/look_and_feel_ui.py:241
msgid "&Transliterate unicode characters to ASCII"
msgstr ""
@@ -7353,10 +7359,6 @@ msgstr ""
msgid "&Disable font size rescaling"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/convert/look_and_feel_ui.py:249
-msgid "&Subset all embedded fonts"
-msgstr ""
-
#: /home/kovid/work/calibre/src/calibre/gui2/convert/lrf_output.py:16
msgid "LRF Output"
msgstr ""
@@ -8263,10 +8265,10 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:116
#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:153
#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:187
-#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:682
-#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:723
-#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:746
-#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:797
+#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:681
+#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:722
+#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:745
+#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:796
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:348
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:356
#: /home/kovid/work/calibre/src/calibre/gui2/library/delegates.py:83
@@ -8279,23 +8281,23 @@ msgid "Undefined"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:130
-#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:754
+#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:753
msgid "star(s)"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:131
-#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:755
+#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:754
msgid "Unrated"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:174
-#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:784
+#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:783
#, python-format
msgid "Set '%s' to today"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:176
-#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:786
+#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:785
#, python-format
msgid "Clear '%s'"
msgstr ""
@@ -8320,35 +8322,35 @@ msgstr ""
msgid "The enumeration \"{0}\" contains an invalid value that will be set to the default"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:637
+#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:636
msgid "Apply changes"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:830
+#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:829
msgid "Remove series"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:833
+#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:832
msgid "Automatically number books"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:836
+#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:835
msgid "Force numbers to start with "
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:906
+#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:905
msgid "The enumeration \"{0}\" contains invalid values that will not appear in the list"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:950
+#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:949
msgid "Remove all tags"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:970
+#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:969
msgid "tags to add"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:977
+#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:976
msgid "tags to remove"
msgstr ""
@@ -9343,7 +9345,7 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/edit_authors_dialog.py:122
#: /home/kovid/work/calibre/src/calibre/gui2/lrf_renderer/main.py:160
#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single_download.py:543
-#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:751
+#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:753
msgid "No matches found"
msgstr ""
@@ -16154,7 +16156,7 @@ msgid "Options to customize the ebook viewer"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/config.py:30
-#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:1146
+#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:1148
msgid "Remember last used window size"
msgstr ""
@@ -16724,83 +16726,73 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:728
#, python-format
-msgid ""
-"%(which)s font size [%(sc)s]\n"
-"Current magnification: %(mag).1f"
+msgid "Current magnification: %(mag).1f"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:731
-msgid "Increase"
-msgstr ""
-
-#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:734
-msgid "Decrease"
-msgstr ""
-
-#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:752
+#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:754
#, python-format
msgid "No matches found for: %s"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:801
+#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:803
msgid "Loading flow..."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:879
+#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:881
#, python-format
msgid "Laying out %s"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:946
+#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:948
#, python-format
msgid "Bookmark #%d"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:950
+#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:952
msgid "Add bookmark"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:951
+#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:953
msgid "Enter title for bookmark:"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:962
+#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:964
msgid "Manage Bookmarks"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:1004
+#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:1006
msgid "Loading ebook..."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:1017
+#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:1019
msgid "Could not open ebook"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:1018
+#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:1020
msgid "Unknown error"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:1133
+#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:1135
msgid "Options to control the ebook viewer"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:1140
+#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:1142
msgid "If specified, viewer window will try to come to the front when started."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:1143
+#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:1145
msgid "If specified, viewer window will try to open full screen when started."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:1148
+#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:1150
msgid "Print javascript alert and console messages to the console"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:1150
+#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:1152
msgid "The position at which to open the specified book. The position is a location as displayed in the top left corner of the viewer."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:1157
+#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:1159
msgid ""
"%prog [options] file\n"
"\n"
@@ -16914,7 +16906,8 @@ msgid "Show"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:958
-msgid "Hide"
+#, python-format
+msgid "Hide %(label)s %(shortcut)s"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:995
@@ -19988,3 +19981,11 @@ msgstr ""
#: /home/kovid/work/calibre/resources/default_tweaks.py:512
msgid "This means that you can make changes and press Enter and your changes will\nnot be overwritten by a matching completion. However, if you wish to use the\ncompletions you will now have to press Tab to select one before pressing\nEnter. Which technique you prefer will depend on the state of metadata in\nyour library and your personal editing style."
msgstr ""
+
+#: /home/kovid/work/calibre/resources/default_tweaks.py:519
+msgid "Recognize numbers inside text when sorting"
+msgstr ""
+
+#: /home/kovid/work/calibre/resources/default_tweaks.py:520
+msgid "This means that when sorting on text fields like title the text \"Book 2\"\nwill sort before the text \"Book 100\". If you want this behavior, set\nnumeric_collation = True note that doing so will cause problems with text\nthat starts with numbers and is a little slower."
+msgstr ""
From 5d67c73f1982646b7706dad63a4f6728a28c49a9 Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Sun, 24 Feb 2013 10:09:23 +0530
Subject: [PATCH 22/31] ...
---
src/calibre/translations/calibre.pot | 16 ++++++++++++++--
src/calibre/utils/localization.py | 3 +++
2 files changed, 17 insertions(+), 2 deletions(-)
diff --git a/src/calibre/translations/calibre.pot b/src/calibre/translations/calibre.pot
index c56c518d1c..f5f97d3059 100644
--- a/src/calibre/translations/calibre.pot
+++ b/src/calibre/translations/calibre.pot
@@ -5,8 +5,8 @@
msgid ""
msgstr ""
"Project-Id-Version: calibre 0.9.20\n"
-"POT-Creation-Date: 2013-02-24 10:04+IST\n"
-"PO-Revision-Date: 2013-02-24 10:04+IST\n"
+"POT-Creation-Date: 2013-02-24 10:08+IST\n"
+"PO-Revision-Date: 2013-02-24 10:08+IST\n"
"Last-Translator: Automatically generated\n"
"Language-Team: LANGUAGE\n"
"MIME-Version: 1.0\n"
@@ -19356,6 +19356,18 @@ msgstr ""
msgid "pm"
msgstr ""
+#: /home/kovid/work/calibre/src/calibre/utils/localization.py:204
+msgid "&Copy"
+msgstr ""
+
+#: /home/kovid/work/calibre/src/calibre/utils/localization.py:205
+msgid "Select All"
+msgstr ""
+
+#: /home/kovid/work/calibre/src/calibre/utils/localization.py:206
+msgid "Copy &Link location"
+msgstr ""
+
#: /home/kovid/work/calibre/src/calibre/utils/pyconsole/console.py:56
msgid "Choose theme (needs restart)"
msgstr ""
diff --git a/src/calibre/utils/localization.py b/src/calibre/utils/localization.py
index 9b49bf687e..1a4a335d52 100644
--- a/src/calibre/utils/localization.py
+++ b/src/calibre/utils/localization.py
@@ -201,6 +201,9 @@ if False:
_('am')
# NOTE: Post Meridian (i.e. like 10:00 pm)
_('pm')
+ _('&Copy')
+ _('Select All')
+ _('Copy &Link location')
_lcase_map = {}
for k in _extra_lang_codes:
From fcf17898d06a26d7467445da94110a30f5269fbc Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Sun, 24 Feb 2013 20:56:16 +0530
Subject: [PATCH 23/31] ...
---
manual/faq.rst | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/manual/faq.rst b/manual/faq.rst
index 24774c8c7d..2d2862e4e6 100644
--- a/manual/faq.rst
+++ b/manual/faq.rst
@@ -616,7 +616,10 @@ or a Remote Desktop solution.
If you must share the actual library, use a file syncing tool like
DropBox or rsync or Microsoft SkyDrive instead of a networked drive. Even with
these tools there is danger of data corruption/loss, so only do this if you are
-willing to live with that risk.
+willing to live with that risk. In particular, be aware that **Google Drive**
+is incompatible with |app|, if you put your |app| library in Google Drive, you
+*will* suffer data loss. See
+`this thread `_ for details.
Content From The Web
---------------------
From 458209bbf91784da61751f0e8e35d1c47450b706 Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Mon, 25 Feb 2013 09:52:39 +0530
Subject: [PATCH 24/31] Democracy Journal by David Nye
---
recipes/democracy_journal.recipe | 27 +++++++++++++++++++++++++++
1 file changed, 27 insertions(+)
create mode 100644 recipes/democracy_journal.recipe
diff --git a/recipes/democracy_journal.recipe b/recipes/democracy_journal.recipe
new file mode 100644
index 0000000000..f02a3b70a8
--- /dev/null
+++ b/recipes/democracy_journal.recipe
@@ -0,0 +1,27 @@
+from calibre.web.feeds.news import BasicNewsRecipe
+import re
+
+class AdvancedUserRecipe1361743898(BasicNewsRecipe):
+ title = u'Democracy Journal'
+ description = '''A journal of ideas. Published quarterly.'''
+ __author__ = u'David Nye'
+ language = 'en'
+ oldest_article = 90
+ max_articles_per_feed = 30
+ no_stylesheets = True
+ auto_cleanup = True
+
+ def parse_index(self):
+ articles = []
+ feeds = []
+ soup = self.index_to_soup("http://www.democracyjournal.org")
+ for x in soup.findAll(href=re.compile("http://www\.democracyjournal\.org/\d*/.*php$")):
+ url = x.get('href')
+ title = self.tag_to_string(x)
+ articles.append({'title':title, 'url':url, 'description':'', 'date':''})
+ feeds.append(('Articles', articles))
+ return feeds
+
+ def print_version(self, url):
+ return url + '?page=all'
+
From 9d8a89d6e5199979c5ef09392b54d03125769d3f Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Mon, 25 Feb 2013 10:07:21 +0530
Subject: [PATCH 25/31] Update Science News
---
recipes/science_news.recipe | 61 ++++++++++++++++++++++---------------
1 file changed, 36 insertions(+), 25 deletions(-)
diff --git a/recipes/science_news.recipe b/recipes/science_news.recipe
index fa24bbadcf..53b451030a 100644
--- a/recipes/science_news.recipe
+++ b/recipes/science_news.recipe
@@ -1,24 +1,38 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
-__copyright__ = '2008, Darko Miletic '
'''
sciencenews.org
'''
from calibre.web.feeds.news import BasicNewsRecipe
-class Sciencenews(BasicNewsRecipe):
- title = u'ScienceNews'
- __author__ = u'Darko Miletic and Sujata Raman'
- description = u"Science News is an award-winning weekly newsmagazine covering the most important research in all fields of science. Its 16 pages each week are packed with short, accurate articles that appeal to both general readers and scientists. Published since 1922, the magazine now reaches about 150,000 subscribers and more than 1 million readers. These are the latest News Items from Science News."
+class ScienceNewsIssue(BasicNewsRecipe):
+ title = u'Science News Recent Issues'
+ __author__ = u'Darko Miletic, Sujata Raman and Starson17'
+ description = u'''Science News is an award-winning weekly
+ newsmagazine covering the most important research in all fields of science.
+ Its 16 pages each week are packed with short, accurate articles that appeal
+ to both general readers and scientists. Published since 1922, the magazine
+ now reaches about 150,000 subscribers and more than 1 million readers.
+ These are the latest News Items from Science News. This recipe downloads
+ the last 30 days worth of articles.'''
+ category = u'Science, Technology, News'
+ publisher = u'Society for Science & the Public'
oldest_article = 30
language = 'en'
-
max_articles_per_feed = 100
no_stylesheets = True
use_embedded_content = False
- auto_cleanup = True
timefmt = ' [%A, %d %B, %Y]'
+ recursions = 1
+ remove_attributes = ['style']
+
+ conversion_options = {'linearize_tables' : True
+ , 'comment' : description
+ , 'tags' : category
+ , 'publisher' : publisher
+ , 'language' : language
+ }
extra_css = '''
.content_description{font-family:georgia ;font-size:x-large; color:#646464 ; font-weight:bold;}
@@ -27,36 +41,33 @@ class Sciencenews(BasicNewsRecipe):
.content_edition{font-family:helvetica,arial ;font-size: xx-small ;}
.exclusive{color:#FF0000 ;}
.anonymous{color:#14487E ;}
- .content_content{font-family:helvetica,arial ;font-size: x-small ; color:#000000;}
- .description{color:#585858;font-family:helvetica,arial ;font-size: xx-small ;}
+ .content_content{font-family:helvetica,arial ;font-size: medium ; color:#000000;}
+ .description{color:#585858;font-family:helvetica,arial ;font-size: large ;}
.credit{color:#A6A6A6;font-family:helvetica,arial ;font-size: xx-small ;}
'''
- #keep_only_tags = [ dict(name='div', attrs={'id':'column_action'}) ]
- #remove_tags_after = dict(name='ul', attrs={'id':'content_functions_bottom'})
- #remove_tags = [
- #dict(name='ul', attrs={'id':'content_functions_bottom'})
- #,dict(name='div', attrs={'id':['content_functions_top','breadcrumb_content']})
- #,dict(name='img', attrs={'class':'icon'})
- #,dict(name='div', attrs={'class': 'embiggen'})
- #]
+ keep_only_tags = [ dict(name='div', attrs={'class':'content_content'}),
+ dict(name='ul', attrs={'id':'toc'})
+ ]
- feeds = [(u"Science News / News Items", u'http://sciencenews.org/index.php/feed/type/news/name/news.rss/view/feed/name/all.rss')]
+ feeds = [(u"Science News Current Issues", u'http://www.sciencenews.org/view/feed/type/edition/name/issues.rss')]
+
+ match_regexps = [
+ r'www.sciencenews.org/view/feature/id/',
+ r'www.sciencenews.org/view/generic/id'
+ ]
def get_cover_url(self):
cover_url = None
index = 'http://www.sciencenews.org/view/home'
soup = self.index_to_soup(index)
link_item = soup.find(name = 'img',alt = "issue")
- print link_item
if link_item:
cover_url = 'http://www.sciencenews.org' + link_item['src'] + '.jpg'
return cover_url
- #def preprocess_html(self, soup):
-
- #for tag in soup.findAll(name=['span']):
- #tag.name = 'div'
-
- #return soup
+ def preprocess_html(self, soup):
+ for tag in soup.findAll(name=['span']):
+ tag.name = 'div'
+ return soup
From 6e2718c02aad396582b544ec6593771889552886 Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Mon, 25 Feb 2013 10:38:30 +0530
Subject: [PATCH 26/31] Edit metadata dialog: When pasting in copied text into
the comments area, you can now choose to discard all formatting. Right click
on the comments area and select 'Paste and Match style' which will paste the
copied text as plain text formatted in the current style.
---
src/calibre/gui2/comments_editor.py | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/src/calibre/gui2/comments_editor.py b/src/calibre/gui2/comments_editor.py
index 39ada6b6cb..41f92b509a 100644
--- a/src/calibre/gui2/comments_editor.py
+++ b/src/calibre/gui2/comments_editor.py
@@ -327,6 +327,13 @@ class EditorWidget(QWebView): # {{{
else:
return QWebView.keyReleaseEvent(self, ev)
+ def contextMenuEvent(self, ev):
+ menu = self.page().createStandardContextMenu()
+ paste = self.pageAction(QWebPage.Paste)
+ for action in menu.actions():
+ if action == paste:
+ menu.insertAction(action, self.pageAction(QWebPage.PasteAndMatchStyle))
+ menu.exec_(ev.globalPos())
# }}}
From fef594c510829d3b53d48e24e25860397bf44cb1 Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Mon, 25 Feb 2013 12:37:21 +0530
Subject: [PATCH 27/31] Driver for Iriver Story EB12. Fixes #1132583 (Private
bug)
---
src/calibre/devices/iriver/driver.py | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/src/calibre/devices/iriver/driver.py b/src/calibre/devices/iriver/driver.py
index 7479c10866..1e3a4e1e37 100644
--- a/src/calibre/devices/iriver/driver.py
+++ b/src/calibre/devices/iriver/driver.py
@@ -22,13 +22,14 @@ class IRIVER_STORY(USBMS):
FORMATS = ['epub', 'fb2', 'pdf', 'djvu', 'txt']
VENDOR_ID = [0x1006]
- PRODUCT_ID = [0x4023, 0x4024, 0x4025, 0x4034]
- BCD = [0x0323, 0x0326]
+ PRODUCT_ID = [0x4023, 0x4024, 0x4025, 0x4034, 0x4037]
+ BCD = [0x0323, 0x0326, 0x226]
VENDOR_NAME = 'IRIVER'
- WINDOWS_MAIN_MEM = ['STORY', 'STORY_EB05', 'STORY_WI-FI', 'STORY_EB07']
+ WINDOWS_MAIN_MEM = ['STORY', 'STORY_EB05', 'STORY_WI-FI', 'STORY_EB07',
+ 'STORY_EB12']
WINDOWS_MAIN_MEM = re.compile(r'(%s)&'%('|'.join(WINDOWS_MAIN_MEM)))
- WINDOWS_CARD_A_MEM = ['STORY', 'STORY_SD']
+ WINDOWS_CARD_A_MEM = ['STORY', 'STORY_SD', 'STORY_EB12_SD']
WINDOWS_CARD_A_MEM = re.compile(r'(%s)&'%('|'.join(WINDOWS_CARD_A_MEM)))
#OSX_MAIN_MEM = 'Kindle Internal Storage Media'
From 6b074ffae6ede4a8e462be1b03e3994708187eb8 Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Mon, 25 Feb 2013 16:09:00 +0530
Subject: [PATCH 28/31] PDF Output: Do not error out when embedding a font that
calibre cannot subset, instead embedthe full font
---
src/calibre/ebooks/pdf/render/fonts.py | 15 ++++++++++-----
src/calibre/ebooks/pdf/render/serialize.py | 2 +-
2 files changed, 11 insertions(+), 6 deletions(-)
diff --git a/src/calibre/ebooks/pdf/render/fonts.py b/src/calibre/ebooks/pdf/render/fonts.py
index e99cc7c218..9a1167021c 100644
--- a/src/calibre/ebooks/pdf/render/fonts.py
+++ b/src/calibre/ebooks/pdf/render/fonts.py
@@ -13,9 +13,10 @@ from operator import itemgetter
from collections import Counter, OrderedDict
from future_builtins import map
+from calibre import as_unicode
from calibre.ebooks.pdf.render.common import (Array, String, Stream,
Dictionary, Name)
-from calibre.utils.fonts.sfnt.subset import pdf_subset
+from calibre.utils.fonts.sfnt.subset import pdf_subset, UnsupportedFont
STANDARD_FONTS = {
'Times-Roman', 'Helvetica', 'Courier', 'Symbol', 'Times-Bold',
@@ -150,12 +151,16 @@ class Font(object):
self.used_glyphs = set()
- def embed(self, objects):
+ def embed(self, objects, debug):
self.font_descriptor['FontFile'+('3' if self.is_otf else '2')
] = objects.add(self.font_stream)
self.write_widths(objects)
self.write_to_unicode(objects)
- pdf_subset(self.metrics.sfnt, self.used_glyphs)
+ try:
+ pdf_subset(self.metrics.sfnt, self.used_glyphs)
+ except UnsupportedFont as e:
+ debug('Subsetting of %s not supported, embedding full font. Error: %s'%(
+ self.metrics.names.get('full_name', 'Unknown'), as_unicode(e)))
if self.is_otf:
self.font_stream.write(self.metrics.sfnt['CFF '].raw)
else:
@@ -221,7 +226,7 @@ class FontManager(object):
}))
return self.std_map[name]
- def embed_fonts(self):
+ def embed_fonts(self, debug):
for font in self.fonts:
- font.embed(self.objects)
+ font.embed(self.objects, debug)
diff --git a/src/calibre/ebooks/pdf/render/serialize.py b/src/calibre/ebooks/pdf/render/serialize.py
index 936cb5f156..fd10e0756f 100644
--- a/src/calibre/ebooks/pdf/render/serialize.py
+++ b/src/calibre/ebooks/pdf/render/serialize.py
@@ -488,7 +488,7 @@ class PDFStream(object):
def end(self):
if self.current_page.getvalue():
self.end_page()
- self.font_manager.embed_fonts()
+ self.font_manager.embed_fonts(self.debug)
inforef = self.objects.add(self.info)
self.links.add_links()
self.objects.pdf_serialize(self.stream)
From 2a419b2a2f39853601f3bc6072e2cf2ed7f79ef4 Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Mon, 25 Feb 2013 17:47:26 +0530
Subject: [PATCH 29/31] E-book viewer: Fix rendering of pages for right-to-left
text in paged mode is reversed. Fixes #1132626 (Ebook Viewer - Reversed
paging in 'page mode' for RTL epubs)
---
resources/compiled_coffeescript.zip | Bin 67342 -> 67910 bytes
src/calibre/ebooks/oeb/display/paged.coffee | 8 ++++++++
2 files changed, 8 insertions(+)
diff --git a/resources/compiled_coffeescript.zip b/resources/compiled_coffeescript.zip
index 7edbd43dc949175cf277bdbc8334c8b2f00910f5..c9ed50891303fb20e5c16b5295525d5a516a0a94 100644
GIT binary patch
delta 526
zcmeC{V>vd7MLNKnnMH&F1oC?$omO!l5*7+&U;tt1$#V5#n-z7c_{=jB^HOqB$iKVZPW%G<*&ODoaL$}NGWu@6VRljO%yA{O1a4TpsU%li;!z#YbI|clhHX917
zIBjMroz^=2HX9??ktbbd}o<>@Nyj2cXiJd+)>MJLyUa!=1=XVhYnRs!;s
zr|)HFU|7h)2;>0(_s&0<
diff --git a/src/calibre/ebooks/oeb/display/paged.coffee b/src/calibre/ebooks/oeb/display/paged.coffee
index aae9aeddd2..2f698966dd 100644
--- a/src/calibre/ebooks/oeb/display/paged.coffee
+++ b/src/calibre/ebooks/oeb/display/paged.coffee
@@ -75,6 +75,13 @@ class PagedDisplay
this.margin_side = margin_side
this.margin_bottom = margin_bottom
+ handle_rtl_body: (body_style) ->
+ if body_style.direction == "rtl"
+ for node in document.body.childNodes
+ if node.nodeType == node.ELEMENT_NODE and window.getComputedStyle(node).direction == "rtl"
+ node.style.setProperty("direction", "rtl")
+ document.body.style.direction = "ltr"
+
layout: (is_single_page=false) ->
# start_time = new Date().getTime()
body_style = window.getComputedStyle(document.body)
@@ -84,6 +91,7 @@ class PagedDisplay
# Check if the current document is a full screen layout like
# cover, if so we treat it specially.
single_screen = (document.body.scrollHeight < window.innerHeight + 75)
+ this.handle_rtl_body(body_style)
first_layout = true
ww = window.innerWidth
From f474d29b1a0a5d95efb98534ade59888183459b2 Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Mon, 25 Feb 2013 18:09:27 +0530
Subject: [PATCH 30/31] ...
---
src/calibre/db/cache.py | 4 ++--
src/calibre/db/write.py | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/calibre/db/cache.py b/src/calibre/db/cache.py
index af22db64d7..e28f32c0f8 100644
--- a/src/calibre/db/cache.py
+++ b/src/calibre/db/cache.py
@@ -615,11 +615,11 @@ class Cache(object):
icon_map=icon_map)
@write_api
- def set_field(self, name, book_id_to_val_map):
+ def set_field(self, name, book_id_to_val_map, allow_case_change=True):
# TODO: Specialize title/authors to also update path
# TODO: Handle updating caches used by composite fields
dirtied = self.fields[name].writer.set_books(
- book_id_to_val_map, self.backend)
+ book_id_to_val_map, self.backend, allow_case_change=allow_case_change)
return dirtied
# }}}
diff --git a/src/calibre/db/write.py b/src/calibre/db/write.py
index 98e58f8594..5b3fc73f0c 100644
--- a/src/calibre/db/write.py
+++ b/src/calibre/db/write.py
@@ -180,7 +180,7 @@ class Writer(object):
if self.name in {'timestamp', 'uuid', 'sort'}:
self.accept_vals = bool
- def set_books(self, book_id_val_map, db):
+ def set_books(self, book_id_val_map, db, allow_case_change=True):
book_id_val_map = {k:self.adapter(v) for k, v in
book_id_val_map.iteritems() if self.accept_vals(v)}
if not book_id_val_map:
From 277ba496bf2d7dc84732c57031ac641d9aea5281 Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Mon, 25 Feb 2013 22:27:46 +0530
Subject: [PATCH 31/31] Geopolityka by chemik111
---
recipes/geopolityka.recipe | 12 ++++++++++++
1 file changed, 12 insertions(+)
create mode 100644 recipes/geopolityka.recipe
diff --git a/recipes/geopolityka.recipe b/recipes/geopolityka.recipe
new file mode 100644
index 0000000000..9749007479
--- /dev/null
+++ b/recipes/geopolityka.recipe
@@ -0,0 +1,12 @@
+from calibre.web.feeds.news import BasicNewsRecipe
+
+class BasicUserRecipe1361379046(BasicNewsRecipe):
+ title = u'Geopolityka.org'
+ language = 'pl'
+ __author__ = 'chemik111'
+ oldest_article = 15
+ max_articles_per_feed = 100
+ auto_cleanup = True
+
+ feeds = [(u'Rss', u'http://geopolityka.org/index.php?format=feed&type=rss')]
+