From af57bdcba51f8a1ccea61fef42b58b4fbb593ddc Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 19 Apr 2010 08:57:33 +0530 Subject: [PATCH 01/16] NPR Music Blogs by cix3 --- resources/recipes/npr_music_blogs.recipe | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 resources/recipes/npr_music_blogs.recipe diff --git a/resources/recipes/npr_music_blogs.recipe b/resources/recipes/npr_music_blogs.recipe new file mode 100644 index 0000000000..940bb20d5b --- /dev/null +++ b/resources/recipes/npr_music_blogs.recipe @@ -0,0 +1,18 @@ +from calibre.web.feeds.news import BasicNewsRecipe + +class nprmusic(BasicNewsRecipe): + title = 'NPR Music Blogs' + __author__ = 'cix3' + timefmt = ' [%b %d, %Y]' + language = 'en' + + oldest_article = 30 + max_articles_per_feed = 100 + no_stylesheets = True + + remove_tags = [dict(name='div', attrs={'id':['logo', 'comments', 'related_objects', 'inset module', 'footer', 'strip_control', 'header', 'navigation']}), dict(name='hr'), dict(name='img')] + + feeds = [ + ('A Blog Supreme', 'http://www.npr.org/blogs/ablogsupreme/index.xml'), + ('All Songs Considered', 'http://www.npr.org/blogs/allsongs/index.xml'), + ('Monitor Mix', 'http://www.npr.org/blogs/monitormix/index.xml')] From 8057e6cc79e83fb8563dc7be91d796efc2dd24ea Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 21 Apr 2010 05:58:53 -0600 Subject: [PATCH 02/16] Virtual Shackles by Darko Miletic --- resources/images/news/virtualshackles.png | Bin 0 -> 1541 bytes resources/recipes/virtualshackles.recipe | 33 ++++++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 resources/images/news/virtualshackles.png create mode 100644 resources/recipes/virtualshackles.recipe diff --git a/resources/images/news/virtualshackles.png b/resources/images/news/virtualshackles.png new file mode 100644 index 0000000000000000000000000000000000000000..986545d91fa86021af45e654d1069526cb264157 GIT binary patch literal 1541 zcmV+g2KxDlP)1vF?>NJNGb}0y0~EDI5ToLTTV0UW7A(R2Ca3MaKoTvJW%N+}j_*2!aT7_>%kMp7WmX zea?5jdu9ZhVuPnn-JlR(uP9OVGVU^t4G9!yktZ&qkhGxdJ0I=Ru7xYwCvYq_{ zTt-GkV+abyVlt7Rev~KITLqp z9{{`q1CebuZf0dT6re_?uYi&dcuC`&H3=ps=h0iPUD)rn0LIUoPeWZD zC08#y6vC|F5~8aSFipr6~}Dc#Oq_j90~wnl366y%jm=2pZiMjs(l9? z4$z<|KB5fRtkzeG_5C~<89CFT05XUofT^mgQwydpiF0@rM1azq>`sXd!9#impycY6 zPKhfY-0vBHg0rVPCEl}YMb7{jiwem(k@8ZJv9N$OA%Pf+3V~-0={qC@-ytEb?v3t* zu*9Rc()<$h^=3xA6N$Tz4{9eZH!@CfYWp^fMTPY7^JDDnISd{@5&vPs?Fp=AGZ%kL z;?Vkan5wJ0WUdylwgPC5H^RoT?nF8_GSj(NRLHcY%TQ~bJ4{xVm&?ZR$^Rd~)zgcQ zb|)}+;-oH@b7`Nh9fw-LDz-`T^Y(6;CvV-{~+)r2hB=$=_`07JwA0 zRe#lTpzL}sMd$ydm&OU-4lW)=mbs90knKxixpMSy_sj+4cAF5uMT7yP$1r2fS_X!V zN2}LkG1YS+F@e04W0YJx-)SRRf%=_b8> z{gACz0_QIzYxzRTPo*MT8#@H>kOYD?aLcUFa>uPI5Tq99D+$kcUGfk~~ya5>VIgB}Sm~G1z z15KZy1Pto++&m~Yjt>gTk$R~q+P{~S=m-uB4WRD+J!;BI`TO@HJh*g@$NATJl$*`n z)9D_?i=fBpPlKg{8r`wcQVqDG)@FWM!z(1=qKyAcP0}6*8zh%>>F24 zFH}R+V!Z_RUz*Ebu0^(3>>w?}cs3Kc(L(snL=uAxG}gA&+{rqLVR8hLvkPuPAt;K1 zAPDUOs77XW zT!8P?>Cg^9+3{pecEvInL4$M`RiE+m2SOFm3pzjTQ3oP#`h!^5J;EOn8wW-zN z#2fTefEZv@(<3>^&;(p-dXnOd)fby5{sRZi-z3;H$6x>e03~!qSaf7zbY(hYa%Ew3 zWdJfTF)%GKG%YeQR4_F Date: Wed, 21 Apr 2010 06:00:28 -0600 Subject: [PATCH 03/16] Updated La Republica --- resources/recipes/la_republica.recipe | 37 ++++++++++++++++++++------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/resources/recipes/la_republica.recipe b/resources/recipes/la_republica.recipe index 3bc1fa5ece..107232daa6 100644 --- a/resources/recipes/la_republica.recipe +++ b/resources/recipes/la_republica.recipe @@ -22,21 +22,36 @@ class LaRepublica(BasicNewsRecipe): language = 'it' timefmt = '[%a, %d %b, %Y]' - oldest_article = 1 + oldest_article = 5 max_articles_per_feed = 100 use_embedded_content = False recursion = 10 remove_javascript = True + def get_article_url(self, article): + link = article.get('id', article.get('guid', None)) + if link is None: + return article + return link + + keep_only_tags = [dict(name='div', attrs={'class':'articolo'}), + dict(name='div', attrs={'class':'body-text'}), + dict(name='div', attrs={'class':'page-content'}), + dict(name='div', attrs={'id':'contA'}) + ] - keep_only_tags = [dict(name='div', attrs={'class':'articolo'})] remove_tags = [ dict(name=['object','link']), dict(name='span',attrs={'class':'linkindice'}), - dict(name='div',attrs={'class':'bottom-mobile'}), - dict(name='div',attrs={'id':['rssdiv','blocco']}) + dict(name='div', attrs={'class':'bottom-mobile'}), + dict(name='div', attrs={'id':['rssdiv','blocco']}), + dict(name='div', attrs={'class':'utility'}), + dict(name='div', attrs={'class':'generalbox'}) ] + remove_tags_after = [ + dict(name='div',attrs={'id':'ugc_linkUpload'}) + ] feeds = [ (u'Repubblica Rilievo', u'http://www.repubblica.it/rss/homepage/rss2.0.xml'), @@ -48,8 +63,12 @@ class LaRepublica(BasicNewsRecipe): (u'Repubblica Tecnologia', u'http://www.repubblica.it/rss/tecnologia/rss2.0.xml'), (u'Repubblica Scuola e Universita', u'http://www.repubblica.it/rss/scuola_e_universita/rss2.0.xml'), (u'Repubblica Ambiente', u'http://www.repubblica.it/rss/ambiente/rss2.0.xml'), - (u'Repubblica Cultura', u'http://www.repubblica.it/rss/spettacoli_e_cultura/rss2.0.xml'), - (u'Repubblica Persone', u'http://www.repubblica.it/rss/persone/rss2.0.xml'), - (u'Repubblica Sport', u'http://www.repubblica.it/rss/sport/rss2.0.xml'), - (u'Repubblica Calcio', u'http://www.repubblica.it/rss/sport/calcio/rss2.0.xml') - ] + (u'Repubblica Cultura', u'http://www.repubblica.it/rss/spettacoli_e_cultura/rss2.0.xml'), + (u'Repubblica Persone', u'http://www.repubblica.it/rss/persone/rss2.0.xml'), + (u'Repubblica Sport', u'http://www.repubblica.it/rss/sport/rss2.0.xml'), + (u'Repubblica Calcio', u'http://www.repubblica.it/rss/sport/calcio/rss2.0.xml'), + (u'Repubblica Motori', u'http://www.repubblica.it/rss/motori/rss2.0.xml'), + (u'Repubblica Roma', u'http://roma.repubblica.it/rss/rss2.0.xml'), + (u'Repubblica Torino', u'http://torino.repubblica.it/rss/rss2.0.xml') + ] + From 1127bb435b58e8fbc3d83da5e4e4ba63209f4730 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 21 Apr 2010 06:23:27 -0600 Subject: [PATCH 04/16] Fix #5335 (guardian receipe outputs pages that contain two html-trees) --- resources/recipes/guardian.recipe | 2 ++ 1 file changed, 2 insertions(+) diff --git a/resources/recipes/guardian.recipe b/resources/recipes/guardian.recipe index f74414a569..344e061c26 100644 --- a/resources/recipes/guardian.recipe +++ b/resources/recipes/guardian.recipe @@ -119,5 +119,7 @@ class Guardian(BasicNewsRecipe): raise NotImplementedError + def postprocess_html(self,soup,first): + return soup.findAll('html')[0] From 5fbb2bae5c0a7d2cf2ec34036f3c10b7c60fc1f9 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 22 Apr 2010 03:05:13 -0600 Subject: [PATCH 05/16] Fix #5329 (database integrity fails in 0.6.48) --- src/calibre/library/database2.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index 9ff1c14576..8f42107944 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -123,10 +123,10 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): self.is_case_sensitive = not iswindows and not isosx and \ not os.path.exists(self.dbpath.replace('metadata.db', 'MeTAdAtA.dB')) SchemaUpgrade.__init__(self) - CustomColumns.__init__(self) self.initialize_dynamic() def initialize_dynamic(self): + CustomColumns.__init__(self) template = '''\ (SELECT {query} FROM books_{table}_link AS link INNER JOIN {table} ON(link.{link_col}={table}.id) WHERE link.book=books.id) @@ -1428,6 +1428,7 @@ books_series_link feeds os.remove(self.dbpath) shutil.copyfile(dest, self.dbpath) self.connect() + self.initialize_dynamic() self.refresh() if os.path.exists(dest): os.remove(dest) From 6febcf355805de34d18dedeebcc8c5fe54cdb3d0 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 22 Apr 2010 03:37:26 -0600 Subject: [PATCH 06/16] MOBI Input: Fix regression that broke detection of covers in MOBI files when converting --- src/calibre/ebooks/mobi/reader.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/calibre/ebooks/mobi/reader.py b/src/calibre/ebooks/mobi/reader.py index 84e6208086..f4161d43de 100644 --- a/src/calibre/ebooks/mobi/reader.py +++ b/src/calibre/ebooks/mobi/reader.py @@ -619,6 +619,7 @@ class MobiReader(object): opf.cover = None cover = opf.cover + cover_copied = None if cover is not None: cover = cover.replace('/', os.sep) if os.path.exists(cover): @@ -626,13 +627,19 @@ class MobiReader(object): if os.path.exists(ncover): os.remove(ncover) shutil.copyfile(cover, ncover) + cover_copied = os.path.abspath(ncover) opf.cover = ncover.replace(os.sep, '/') manifest = [(htmlfile, 'application/xhtml+xml'), (os.path.abspath('styles.css'), 'text/css')] bp = os.path.dirname(htmlfile) + added = set([]) for i in getattr(self, 'image_names', []): - manifest.append((os.path.join(bp, 'images/', i), 'image/jpeg')) + path = os.path.join(bp, 'images', i) + added.add(path) + manifest.append((path, 'image/jpeg')) + if cover_copied is not None: + manifest.append((cover_copied, 'image/jpeg')) opf.create_manifest(manifest) opf.create_spine([os.path.basename(htmlfile)]) From c937c7cdce2d45e7f2aaea701ff124b71f6ad64d Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 22 Apr 2010 03:50:37 -0600 Subject: [PATCH 07/16] Fix #5342 (Calibre viewer no longer displays covers) --- src/calibre/ebooks/oeb/iterator.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/calibre/ebooks/oeb/iterator.py b/src/calibre/ebooks/oeb/iterator.py index 87ce8683a9..2312ea308b 100644 --- a/src/calibre/ebooks/oeb/iterator.py +++ b/src/calibre/ebooks/oeb/iterator.py @@ -212,11 +212,12 @@ class EbookIterator(object): cover = self.opf.cover if self.ebook_ext in ('lit', 'mobi', 'prc', 'opf') and cover: - cfile = os.path.join(os.path.dirname(self.spine[0]), - 'calibre_iterator_cover.html') - chtml = (TITLEPAGE%cover).encode('utf-8') + cfile = os.path.join(self.base, 'calibre_iterator_cover.html') + chtml = (TITLEPAGE%os.path.relpath(cover, self.base).replace(os.sep, + '/')).encode('utf-8') open(cfile, 'wb').write(chtml) - self.spine[0:0] = [SpineItem(cfile)] + self.spine[0:0] = [SpineItem(cfile, + mime_type='application/xhtml+xml')] self.delete_on_exit.append(cfile) if self.opf.path_to_html_toc is not None and \ From 0d1031ab543fb47275750610af3f542c00c0d57e Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 22 Apr 2010 04:40:22 -0600 Subject: [PATCH 08/16] Fix #5334 (Alphabetizing problem) --- src/calibre/web/feeds/recipes/model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/web/feeds/recipes/model.py b/src/calibre/web/feeds/recipes/model.py index d7da358765..1584908237 100644 --- a/src/calibre/web/feeds/recipes/model.py +++ b/src/calibre/web/feeds/recipes/model.py @@ -113,7 +113,7 @@ class NewsItem(NewsTreeItem): return NONE def __cmp__(self, other): - return cmp(self.title, getattr(other, 'title', '')) + return cmp(self.title.lower(), getattr(other, 'title', '').lower()) class RecipeModel(QAbstractItemModel, SearchQueryParser): From d4b4e1902dd22353d7a2f716afe2fb331ce15100 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 22 Apr 2010 04:43:56 -0600 Subject: [PATCH 09/16] MOBI Input: Don't fail when the MOBI metadata species a cover that does not exist. Fixes #5333 (Coversion Failed) --- src/calibre/ebooks/mobi/reader.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/ebooks/mobi/reader.py b/src/calibre/ebooks/mobi/reader.py index f4161d43de..ace60673d7 100644 --- a/src/calibre/ebooks/mobi/reader.py +++ b/src/calibre/ebooks/mobi/reader.py @@ -628,7 +628,7 @@ class MobiReader(object): os.remove(ncover) shutil.copyfile(cover, ncover) cover_copied = os.path.abspath(ncover) - opf.cover = ncover.replace(os.sep, '/') + opf.cover = ncover.replace(os.sep, '/') manifest = [(htmlfile, 'application/xhtml+xml'), (os.path.abspath('styles.css'), 'text/css')] From d128c2ef210bdfa2701acd6502b6bbee87965ca0 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 22 Apr 2010 05:06:23 -0600 Subject: [PATCH 10/16] Fix rendering of ratings column on linux --- src/calibre/gui2/library.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/calibre/gui2/library.py b/src/calibre/gui2/library.py index 746d97ca32..3fe1cbc908 100644 --- a/src/calibre/gui2/library.py +++ b/src/calibre/gui2/library.py @@ -7,9 +7,9 @@ from math import cos, sin, pi from contextlib import closing from PyQt4.QtGui import QTableView, QAbstractItemView, QColor, \ - QItemDelegate, QPainterPath, QLinearGradient, QBrush, \ - QPen, QStyle, QPainter, \ - QImage, QApplication, QMenu, \ + QPainterPath, QLinearGradient, QBrush, \ + QPen, QStyle, QPainter, QStyleOptionViewItemV4, \ + QImage, QMenu, \ QStyledItemDelegate, QCompleter from PyQt4.QtCore import QAbstractTableModel, QVariant, Qt, pyqtSignal, \ SIGNAL, QObject, QSize, QModelIndex, QDate @@ -28,14 +28,15 @@ from calibre.ebooks.metadata import string_to_authors, fmt_sidx, \ from calibre.utils.config import tweaks from calibre.utils.date import dt_factory, qt_to_dt, isoformat -class LibraryDelegate(QItemDelegate): +class LibraryDelegate(QStyledItemDelegate): COLOR = QColor("blue") SIZE = 16 PEN = QPen(COLOR, 1, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin) def __init__(self, parent): - QItemDelegate.__init__(self, parent) + QStyledItemDelegate.__init__(self, parent) self._parent = parent + self.dummy = QModelIndex() self.star_path = QPainterPath() self.star_path.moveTo(90, 50) for i in range(1, 5): @@ -54,6 +55,9 @@ class LibraryDelegate(QItemDelegate): return QSize(5*(self.SIZE), self.SIZE+4) def paint(self, painter, option, index): + style = self._parent.style() + option = QStyleOptionViewItemV4(option) + self.initStyleOption(option, self.dummy) num = index.model().data(index, Qt.DisplayRole).toInt()[0] def draw_star(): painter.save() @@ -66,11 +70,10 @@ class LibraryDelegate(QItemDelegate): painter.save() if hasattr(QStyle, 'CE_ItemViewItem'): - QApplication.style().drawControl(QStyle.CE_ItemViewItem, option, + style.drawControl(QStyle.CE_ItemViewItem, option, painter, self._parent) elif option.state & QStyle.State_Selected: painter.fillRect(option.rect, option.palette.highlight()) - self.drawFocus(painter, option, option.rect) try: painter.setRenderHint(QPainter.Antialiasing) painter.setClipRect(option.rect) @@ -89,7 +92,7 @@ class LibraryDelegate(QItemDelegate): painter.restore() def createEditor(self, parent, option, index): - sb = QItemDelegate.createEditor(self, parent, option, index) + sb = QStyledItemDelegate.createEditor(self, parent, option, index) sb.setMinimum(0) sb.setMaximum(5) return sb From cc75e7291b89aa1195dc6606eb0871f661f07526 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 22 Apr 2010 06:03:36 -0600 Subject: [PATCH 11/16] ... --- src/calibre/gui2/dialogs/scheduler.py | 1 + src/calibre/gui2/viewer/main.py | 1 + 2 files changed, 2 insertions(+) diff --git a/src/calibre/gui2/dialogs/scheduler.py b/src/calibre/gui2/dialogs/scheduler.py index 2fd26cffa1..296d01ecdd 100644 --- a/src/calibre/gui2/dialogs/scheduler.py +++ b/src/calibre/gui2/dialogs/scheduler.py @@ -29,6 +29,7 @@ class SchedulerDialog(QDialog, Ui_Dialog): self.recipe_model.do_refresh() self.search = SearchBox2(self) + self.search.setMinimumContentsLength(25) self.search.initialize('scheduler_search_history') self.recipe_box.layout().insertWidget(0, self.search) self.connect(self.search, SIGNAL('search(PyQt_PyObject,PyQt_PyObject)'), diff --git a/src/calibre/gui2/viewer/main.py b/src/calibre/gui2/viewer/main.py index f27f17e6ca..689613111e 100644 --- a/src/calibre/gui2/viewer/main.py +++ b/src/calibre/gui2/viewer/main.py @@ -194,6 +194,7 @@ class EbookViewer(MainWindow, Ui_EbookViewer): self.tool_bar2.insertSeparator(self.action_find_next) self.setFocusPolicy(Qt.StrongFocus) self.search = SearchBox2(self) + self.search.setMinimumContentsLength(20) self.search.initialize('viewer_search_history') self.search.setToolTip(_('Search for text in book')) self.search.setMinimumWidth(200) From eefa1d58c2e48820c31da89ff674355481db6899 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 22 Apr 2010 06:13:02 -0600 Subject: [PATCH 12/16] Fix USA Today --- resources/recipes/usatoday.recipe | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/resources/recipes/usatoday.recipe b/resources/recipes/usatoday.recipe index 5b036d145d..bd47262563 100644 --- a/resources/recipes/usatoday.recipe +++ b/resources/recipes/usatoday.recipe @@ -377,8 +377,9 @@ class USAToday(BasicNewsRecipe): if byline: byline['class'] = 'byline' # Replace comma with middot - byline.contents[0].replaceWith(re.sub(","," ·", byline.renderContents())) - return byline.renderContents() + byline.contents[0].replaceWith(re.sub(u",", u" ·", + byline.renderContents(encoding=None))) + return byline.renderContents(encoding=None) else : paras = soup.findAll(text=True) for para in paras: From a6945bdadbc9a4022d45e07539a204945ed8c410 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 22 Apr 2010 07:54:21 -0600 Subject: [PATCH 13/16] Fix #5336 (Text within angle brackets disappears in .mobi books) --- src/calibre/__init__.py | 22 +++++++++++++--------- src/calibre/ebooks/mobi/reader.py | 7 ++++++- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/calibre/__init__.py b/src/calibre/__init__.py index d21fcc8e87..6d104650bc 100644 --- a/src/calibre/__init__.py +++ b/src/calibre/__init__.py @@ -399,38 +399,42 @@ def my_unichr(num): except ValueError: return u'?' -def entity_to_unicode(match, exceptions=[], encoding='cp1252'): +def entity_to_unicode(match, exceptions=[], encoding='cp1252', + result_exceptions={}): ''' @param match: A match object such that '&'+match.group(1)';' is the entity. @param exceptions: A list of entities to not convert (Each entry is the name of the entity, for e.g. 'apos' or '#1234' @param encoding: The encoding to use to decode numeric entities between 128 and 256. If None, the Unicode UCS encoding is used. A common encoding is cp1252. ''' + def check(ch): + return result_exceptions.get(ch, ch) + ent = match.group(1) if ent in exceptions: return '&'+ent+';' if ent == 'apos': - return "'" + return check("'") if ent == 'hellips': ent = 'hellip' - if ent.startswith(u'#x'): + if ent.lower().startswith(u'#x'): num = int(ent[2:], 16) if encoding is None or num > 255: - return my_unichr(num) - return chr(num).decode(encoding) + return check(my_unichr(num)) + return check(chr(num).decode(encoding)) if ent.startswith(u'#'): try: num = int(ent[1:]) except ValueError: return '&'+ent+';' if encoding is None or num > 255: - return my_unichr(num) + return check(my_unichr(num)) try: - return chr(num).decode(encoding) + return check(chr(num).decode(encoding)) except UnicodeDecodeError: - return my_unichr(num) + return check(my_unichr(num)) try: - return my_unichr(name2codepoint[ent]) + return check(my_unichr(name2codepoint[ent])) except KeyError: return '&'+ent+';' diff --git a/src/calibre/ebooks/mobi/reader.py b/src/calibre/ebooks/mobi/reader.py index ace60673d7..1b266740d7 100644 --- a/src/calibre/ebooks/mobi/reader.py +++ b/src/calibre/ebooks/mobi/reader.py @@ -303,7 +303,12 @@ class MobiReader(object): for pat in ENCODING_PATS: self.processed_html = pat.sub('', self.processed_html) e2u = functools.partial(entity_to_unicode, - exceptions=['lt', 'gt', 'amp', 'apos', 'quot', '#60', '#62']) + result_exceptions={ + '<' : u'<', + '>' : u'>', + '&' : u'&', + '"' : u'"', + "'" : u'''}) self.processed_html = re.sub(r'&(\S+?);', e2u, self.processed_html) self.extract_images(processed_records, output_dir) From d8a500f19640043e9a1d75aea353328fc3fa6511 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 22 Apr 2010 09:50:00 -0600 Subject: [PATCH 14/16] Support for the SpringDesign Alex --- src/calibre/__init__.py | 13 ++++++++++--- src/calibre/customize/builtins.py | 5 +++-- src/calibre/devices/hanvon/driver.py | 16 ++++++++++++++++ src/calibre/manual/faq.rst | 2 +- 4 files changed, 30 insertions(+), 6 deletions(-) diff --git a/src/calibre/__init__.py b/src/calibre/__init__.py index 6d104650bc..2babb9182b 100644 --- a/src/calibre/__init__.py +++ b/src/calibre/__init__.py @@ -402,10 +402,17 @@ def my_unichr(num): def entity_to_unicode(match, exceptions=[], encoding='cp1252', result_exceptions={}): ''' - @param match: A match object such that '&'+match.group(1)';' is the entity. - @param exceptions: A list of entities to not convert (Each entry is the name of the entity, for e.g. 'apos' or '#1234' - @param encoding: The encoding to use to decode numeric entities between 128 and 256. + :param match: A match object such that '&'+match.group(1)';' is the entity. + + :param exceptions: A list of entities to not convert (Each entry is the name of the entity, for e.g. 'apos' or '#1234' + + :param encoding: The encoding to use to decode numeric entities between 128 and 256. If None, the Unicode UCS encoding is used. A common encoding is cp1252. + + :param result_exceptions: A mapping of characters to entities. If the result + is in result_exceptions, result_exception[result] is returned instead. + Convenient way to specify exception for things like < or > that can be + specified by various actual entities. ''' def check(ch): return result_exceptions.get(ch, ch) diff --git a/src/calibre/customize/builtins.py b/src/calibre/customize/builtins.py index c1d779bed9..44f4c61ca9 100644 --- a/src/calibre/customize/builtins.py +++ b/src/calibre/customize/builtins.py @@ -444,7 +444,7 @@ from calibre.devices.eslick.driver import ESLICK from calibre.devices.nuut2.driver import NUUT2 from calibre.devices.iriver.driver import IRIVER_STORY from calibre.devices.binatone.driver import README -from calibre.devices.hanvon.driver import N516, EB511 +from calibre.devices.hanvon.driver import N516, EB511, ALEX from calibre.devices.edge.driver import EDGE from calibre.devices.teclast.driver import TECLAST_K3 from calibre.devices.sne.driver import SNE @@ -526,7 +526,8 @@ plugins += [ ELONEX, TECLAST_K3, EDGE, - SNE + SNE, + ALEX ] plugins += [x for x in list(locals().values()) if isinstance(x, type) and \ x.__name__.endswith('MetadataReader')] diff --git a/src/calibre/devices/hanvon/driver.py b/src/calibre/devices/hanvon/driver.py index d4f6e87d06..1e76b62eb6 100644 --- a/src/calibre/devices/hanvon/driver.py +++ b/src/calibre/devices/hanvon/driver.py @@ -34,6 +34,22 @@ class N516(USBMS): EBOOK_DIR_MAIN = 'e_book' SUPPORTS_SUB_DIRS = True +class ALEX(N516): + + name = 'Alex driver' + gui_name = 'SpringDesign Alex' + description = _('Communicate with the SpringDesign Alex eBook reader.') + author = 'Kovid Goyal' + + FORMATS = ['epub', 'pdf'] + VENDOR_NAME = 'ALEX' + WINDOWS_MAIN_MEM = 'READER' + + MAIN_MEMORY_VOLUME_LABEL = 'Alex Internal Memory' + + EBOOK_DIR_MAIN = 'eBooks' + SUPPORTS_SUB_DIRS = True + class EB511(USBMS): name = 'Elonex EB 511 driver' gui_name = 'EB 511' diff --git a/src/calibre/manual/faq.rst b/src/calibre/manual/faq.rst index b88c4474df..7c2f4ce49e 100644 --- a/src/calibre/manual/faq.rst +++ b/src/calibre/manual/faq.rst @@ -81,7 +81,7 @@ Device Integration What devices does |app| support? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -At the moment |app| has full support for the SONY PRS 300/500/505/600/700/900, Barnes & Noble Nook, Cybook Gen 3/Opus, Amazon Kindle 1/2/DX, Entourage Edge, Longshine ShineBook, Ectaco Jetbook, BeBook/BeBook Mini, Irex Illiad/DR1000, Foxit eSlick, PocketBook 360, Italica, eClicto, Iriver Story, Airis dBook, Hanvon N515, Binatone Readme, Teclast K3, various Android phones and the iPhone. In addition, using the :guilabel:`Save to disk` function you can use it with any ebook reader that exports itself as a USB disk. +At the moment |app| has full support for the SONY PRS 300/500/505/600/700/900, Barnes & Noble Nook, Cybook Gen 3/Opus, Amazon Kindle 1/2/DX, Entourage Edge, Longshine ShineBook, Ectaco Jetbook, BeBook/BeBook Mini, Irex Illiad/DR1000, Foxit eSlick, PocketBook 360, Italica, eClicto, Iriver Story, Airis dBook, Hanvon N515, Binatone Readme, Teclast K3, SpringDesign Alex, various Android phones and the iPhone. In addition, using the :guilabel:`Save to disk` function you can use it with any ebook reader that exports itself as a USB disk. How can I help get my device supported in |app|? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ From bb74e6afe0b6702c446c8cec0e4103ea847c3b88 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 22 Apr 2010 10:16:12 -0600 Subject: [PATCH 15/16] Fix #3086 (Title Case error) --- src/calibre/gui2/widgets.py | 3 +- src/calibre/utils/titlecase.py | 94 ++++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+), 1 deletion(-) create mode 100755 src/calibre/utils/titlecase.py diff --git a/src/calibre/gui2/widgets.py b/src/calibre/gui2/widgets.py index c7031992e8..586966d94f 100644 --- a/src/calibre/gui2/widgets.py +++ b/src/calibre/gui2/widgets.py @@ -551,7 +551,8 @@ class LineEditECM(object): self.setText(unicode(self.text()).swapcase()) def title_case(self): - self.setText(unicode(self.text()).title()) + from calibre.utils.titlecase import titlecase + self.setText(titlecase(unicode(self.text()))) class EnLineEdit(LineEditECM, QLineEdit): diff --git a/src/calibre/utils/titlecase.py b/src/calibre/utils/titlecase.py new file mode 100755 index 0000000000..3ead4848fd --- /dev/null +++ b/src/calibre/utils/titlecase.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Original Perl version by: John Gruber http://daringfireball.net/ 10 May 2008 +Python version by Stuart Colville http://muffinresearch.co.uk +License: http://www.opensource.org/licenses/mit-license.php +""" + +import re + +__all__ = ['titlecase'] +__version__ = '0.5' + +SMALL = 'a|an|and|as|at|but|by|en|for|if|in|of|on|or|the|to|v\.?|via|vs\.?' +PUNCT = r"""!"#$%&'‘()*+,\-./:;?@[\\\]_`{|}~""" + +SMALL_WORDS = re.compile(r'^(%s)$' % SMALL, re.I) +INLINE_PERIOD = re.compile(r'[a-z][.][a-z]', re.I) +UC_ELSEWHERE = re.compile(r'[%s]*?[a-zA-Z]+[A-Z]+?' % PUNCT) +CAPFIRST = re.compile(r"^[%s]*?([A-Za-z])" % PUNCT) +SMALL_FIRST = re.compile(r'^([%s]*)(%s)\b' % (PUNCT, SMALL), re.I) +SMALL_LAST = re.compile(r'\b(%s)[%s]?$' % (SMALL, PUNCT), re.I) +SUBPHRASE = re.compile(r'([:.;?!][ ])(%s)' % SMALL) +APOS_SECOND = re.compile(r"^[dol]{1}['‘]{1}[a-z]+$", re.I) +ALL_CAPS = re.compile(r'^[A-Z\s%s]+$' % PUNCT) +UC_INITIALS = re.compile(r"^(?:[A-Z]{1}\.{1}|[A-Z]{1}\.{1}[A-Z]{1})+$") +MAC_MC = re.compile(r"^([Mm]a?c)(\w+)") + +def titlecase(text): + + """ + Titlecases input text + + This filter changes all words to Title Caps, and attempts to be clever + about *un*capitalizing SMALL words like a/an/the in the input. + + The list of "SMALL words" which are not capped comes from + the New York Times Manual of Style, plus 'vs' and 'v'. + + """ + + all_caps = ALL_CAPS.match(text) + + words = re.split('\s', text) + line = [] + for word in words: + if all_caps: + if UC_INITIALS.match(word): + line.append(word) + continue + else: + word = word.lower() + + if APOS_SECOND.match(word): + word = word.replace(word[0], word[0].upper()) + word = word.replace(word[2], word[2].upper()) + line.append(word) + continue + if INLINE_PERIOD.search(word) or UC_ELSEWHERE.match(word): + line.append(word) + continue + if SMALL_WORDS.match(word): + line.append(word.lower()) + continue + + match = MAC_MC.match(word) + if match: + line.append("%s%s" % (match.group(1).capitalize(), + match.group(2).capitalize())) + continue + + hyphenated = [] + for item in word.split('-'): + hyphenated.append(CAPFIRST.sub(lambda m: m.group(0).upper(), item)) + line.append("-".join(hyphenated)) + + + result = " ".join(line) + + result = SMALL_FIRST.sub(lambda m: '%s%s' % ( + m.group(1), + m.group(2).capitalize() + ), result) + + result = SMALL_LAST.sub(lambda m: m.group(0).capitalize(), result) + + result = SUBPHRASE.sub(lambda m: '%s%s' % ( + m.group(1), + m.group(2).capitalize() + ), result) + + return result + From a4ff20048856bd6be385dbec54e02c4c906b4ee1 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 22 Apr 2010 13:56:17 -0600 Subject: [PATCH 16/16] Kurier by Darko Miletic --- resources/images/news/kurier.png | Bin 0 -> 658 bytes resources/recipes/kurier.recipe | 50 +++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 resources/images/news/kurier.png create mode 100644 resources/recipes/kurier.recipe diff --git a/resources/images/news/kurier.png b/resources/images/news/kurier.png new file mode 100644 index 0000000000000000000000000000000000000000..f11c1dca119eda2fd8671705b582149311f93e25 GIT binary patch literal 658 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b zK-vS0-A-oPfdtD69Mgd`SU*F|v9*U87#MeZx;TbdoL)M~*E`x#q;3Apd;H#4TpgK3 zt9re}^?pZdY5d^k`N4k6Vbu(s+c_~iHt32iDb?L2(B$2m(8TSkF?HP*GkLW-m*V`s z?&|zmOLlFjH7|a~^L(c9dzq=M5A9PJ_V}ncWV^61iYO>$tX1rA{Zl>V+6o1`S+hSg zGHD2g1o-S|bItu}aQ$MViPQoS=anI^7wp>Vnp-V>y|nPs%dmZ0KDDks+$?s1{mN^O z55^N89Q|Ws-paJ}YpIQbkTm0h9lK6d>a~Y)HQOJPWH@`;dw=Ndysa