diff --git a/Changelog.yaml b/Changelog.yaml index a17cb4b82f..31908315f3 100644 --- a/Changelog.yaml +++ b/Changelog.yaml @@ -19,6 +19,54 @@ # new recipes: # - title: +- version: 0.9.4 + date: 2012-10-26 + + new features: + - title: "Conversion: Add an option to embed a font family into the book." + description: "The embedded font is used as the base font for all text that does not specify its own font family in the input document. Works only with output formats that support font embedding, principally EPUB/AZW3. Option is found under Look & Feel in the conversion dialog. You can ensure that the font is used for all text, regardless of the input document's styles by filtering out font family styles via the Filter Style Information option in the Conversion dialog." + type: major + + - title: "When changing the title/author of a book, use hard links instead of copying the books' files, for a large speedup. Only works on filesystems that support hardlinks." + + - title: "Linux installer: Resume interrupted downloads and verify the SHA-512 signature of the downloaded file before installing it." + + bug fixes: + - title: "Windows: Check if any of the files of a book are in use before changing the title/author, this prevents the creation of duplicate files if one of the files is open in another program" + + - title: "Kobo driver: Fix the ondevice status for some books getting lost." + tickets: [1069403] + + - title: "Catalogs: Fix regression that broke use of prefix rules." + tickets: [1070086] + + - title: "Tag Browser: Fix sorting incorrect for accented letters" + tickets: [1069835] + + - title: "Make the bundled Liberation fonts available on all platforms for embedding" + + - title: "Use mimetype for fonts from the EPUB 3 specification" + + - title: "Get Books: Handle website change that broke the SONY Store plugin" + + - title: "Generate cover: If the default font cannot render characters in the metadata (for example for east asian languages) try to automatically find a font on the system that is capable of rendering the characters" + + - title: "Fix regression that broke certain types of CSS selectors." + tickets: [1068937] + + - title: "Use font-weight:bold instead of font-weight:bolder for the and tags as ADE cant handle bolder when embedded fonts are used" + + improved recipes: + - New York Post + - PC World + - TIME Magazine + - Associated Press + + new recipes: + - title: Yazihane + author: A Erdogan + + - version: 0.9.3 date: 2012-10-19 diff --git a/manual/images/sg_pref.jpg b/manual/images/sg_pref.jpg deleted file mode 100644 index 8bab672c25..0000000000 Binary files a/manual/images/sg_pref.jpg and /dev/null differ diff --git a/manual/images/sg_pref.png b/manual/images/sg_pref.png new file mode 100644 index 0000000000..59faa5f3e9 Binary files /dev/null and b/manual/images/sg_pref.png differ diff --git a/manual/sub_groups.rst b/manual/sub_groups.rst index 2b5c3b3856..c08df9a901 100644 --- a/manual/sub_groups.rst +++ b/manual/sub_groups.rst @@ -65,7 +65,7 @@ You create the custom column in the usual way, using Preferences -> Add your own Then after restarting |app|, you must tell |app| that the column is to be treated as a hierarchy. Go to Preferences -> Look and Feel -> Tag Browser and enter the lookup name "#genre" into the "Categories with hierarchical items" box. Press Apply, and you are done with setting up. -.. image:: images/sg_pref.jpg +.. image:: images/sg_pref.png :align: center At the point there are no genres in the column. We are left with the last step: how to apply a genre to a book. A genre does not exist in |app| until it appears on at least one book. To learn how to apply a genre for the first time, we must go into some detail about what a genre looks like in the metadata for a book. diff --git a/recipes/atlantic.recipe b/recipes/atlantic.recipe index 928f1343b3..55e02b2ad1 100644 --- a/recipes/atlantic.recipe +++ b/recipes/atlantic.recipe @@ -38,8 +38,10 @@ class TheAtlantic(BasicNewsRecipe): self.timefmt = ' [%s]'%ds cover = soup.find('img', src=True, attrs={'class':'cover'}) + if cover is not None: - self.cover_url = cover['src'].replace(' ', '%20') + self.cover_url = re.sub('\s','%20',re.sub('jpg.*','jpg',cover['src'])) + self.log(self.cover_url) feeds = [] seen_titles = set([]) @@ -47,18 +49,16 @@ class TheAtlantic(BasicNewsRecipe): section_title = self.tag_to_string(section.find('h2')) self.log('Found section:', section_title) articles = [] - for post in section.findAll('div', attrs={'class':lambda x : x and - 'post' in x}): - h = post.find(['h3', 'h4']) - title = self.tag_to_string(h) + for post in section.findAll('h3', attrs={'class':'headline'}): + a = post.find('a', href=True) + title = self.tag_to_string(a) if title in seen_titles: continue seen_titles.add(title) - a = post.find('a', href=True) url = a['href'] if url.startswith('/'): url = 'http://www.theatlantic.com'+url - p = post.find('p', attrs={'class':'dek'}) + p = post.parent.find('p', attrs={'class':'dek'}) desc = None self.log('\tFound article:', title, 'at', url) if p is not None: @@ -69,19 +69,29 @@ class TheAtlantic(BasicNewsRecipe): if articles: feeds.append((section_title, articles)) - poems = [] - self.log('Found section: Poems') - pd = soup.find('h2', text='Poetry').parent.parent - for poem in pd.findAll('h4'): - title = self.tag_to_string(poem) - url = poem.find('a')['href'] - if url.startswith('/'): - url = 'http://www.theatlantic.com' + url - self.log('\tFound article:', title, 'at', url) - poems.append({'title':title, 'url':url, 'description':'', - 'date':''}) - if poems: - feeds.append(('Poems', poems)) + rightContent=soup.find('div', attrs = {'class':'rightContent'}) + for module in rightContent.findAll('div', attrs={'class':'module'}): + section_title = self.tag_to_string(module.find('h2')) + articles = [] + for post in module.findAll('div', attrs={'class':'post'}): + a = post.find('a', href=True) + title = self.tag_to_string(a) + if title in seen_titles: + continue + seen_titles.add(title) + url = a['href'] + if url.startswith('/'): + url = 'http://www.theatlantic.com'+url + p = post.parent.find('p', attrs={'class':'dek'}) + desc = None + self.log('\tFound article:', title, 'at', url) + if p is not None: + desc = self.tag_to_string(p) + self.log('\t\t', desc) + articles.append({'title':title, 'url':url, 'description':desc, 'date':''}) + if articles: + feeds.append((section_title, articles)) + return feeds @@ -100,4 +110,3 @@ class TheAtlantic(BasicNewsRecipe): table.replaceWith(div) return soup - diff --git a/recipes/financial_times_uk.recipe b/recipes/financial_times_uk.recipe index 4e5b522ae9..e2b69f4987 100644 --- a/recipes/financial_times_uk.recipe +++ b/recipes/financial_times_uk.recipe @@ -1,5 +1,5 @@ __license__ = 'GPL v3' -__copyright__ = '2010-2012, Darko Miletic ' +__copyright__ = '2010-2011, Darko Miletic ' ''' www.ft.com/uk-edition ''' @@ -42,24 +42,18 @@ class FinancialTimes(BasicNewsRecipe): def get_browser(self): br = BasicNewsRecipe.get_browser() br.open(self.INDEX) - if self.username is not None and self.password is not None: - br.open(self.LOGIN2) - br.select_form(name='loginForm') - br['username'] = self.username - br['password'] = self.password - br.submit() + br.open(self.LOGIN) + br.select_form(name='loginForm') + br['username'] = self.username + br['password'] = self.password + br.submit() return br keep_only_tags = [ - dict(name='div' , attrs={'class':['fullstory fullstoryHeader', 'ft-story-header']}) - ,dict(name='div' , attrs={'class':'standfirst'}) - ,dict(name='div' , attrs={'id' :'storyContent'}) - ,dict(name='div' , attrs={'class':['ft-story-body','index-detail']}) - ,dict(name='div' , attrs={'class':['ft-story-body','index-detail']}) - ,dict(name='h2' , attrs={'class':'entry-title'} ) - ,dict(name='span', attrs={'class':lambda x: x and 'posted-on' in x.split()} ) - ,dict(name='span', attrs={'class':'author_byline'} ) - ,dict(name='div' , attrs={'class':'entry-content'} ) + dict(name='div', attrs={'class':['fullstory fullstoryHeader', 'ft-story-header']}) + ,dict(name='div', attrs={'class':'standfirst'}) + ,dict(name='div', attrs={'id' :'storyContent'}) + ,dict(name='div', attrs={'class':['ft-story-body','index-detail']}) ] remove_tags = [ dict(name='div', attrs={'id':'floating-con'}) @@ -88,17 +82,21 @@ class FinancialTimes(BasicNewsRecipe): if self.test and count > 2: return articles rawlink = item['href'] - url = rawlink - if not rawlink.startswith('http://'): - url = self.PREFIX + rawlink - urlverified = self.browser.open_novisit(url).geturl() # resolve redirect. + if rawlink.startswith('http://'): + url = rawlink + else: + url = self.PREFIX + rawlink + try: + urlverified = self.browser.open_novisit(url).geturl() # resolve redirect. + except: + continue title = self.tag_to_string(item) date = strftime(self.timefmt) articles.append({ - 'title' :title - ,'date' :date - ,'url' :urlverified - ,'description':'' + 'title' :title + ,'date' :date + ,'url' :urlverified + ,'description':'' }) return articles @@ -110,20 +108,21 @@ class FinancialTimes(BasicNewsRecipe): wide = soup.find('div',attrs={'class':'wide'}) if not wide: return feeds - allsections = wide.findAll(attrs={'class':lambda x: x and 'footwell' in x.split()}) - if not allsections: + strest = wide.findAll('h3', attrs={'class':'section'}) + if not strest: return feeds + st = wide.findAll('h4',attrs={'class':'section-no-arrow'}) + if st: + st.extend(strest) count = 0 - for item in allsections: + for item in st: count = count + 1 if self.test and count > 2: return feeds - fitem = item.h3 - if not fitem: - fitem = item.h4 - ftitle = self.tag_to_string(fitem) + ftitle = self.tag_to_string(item) self.report_progress(0, _('Fetching feed')+' %s...'%(ftitle)) - feedarts = self.get_artlinks(item.ul) + if item.parent.ul is not None: + feedarts = self.get_artlinks(item.parent.ul) feeds.append((ftitle,feedarts)) return feeds @@ -157,7 +156,7 @@ class FinancialTimes(BasicNewsRecipe): def get_cover_url(self): cdate = datetime.date.today() if cdate.isoweekday() == 7: - cdate -= datetime.timedelta(days=1) + cdate -= datetime.timedelta(days=1) return cdate.strftime('http://specials.ft.com/vtf_pdf/%d%m%y_FRONT1_LON.pdf') def get_obfuscated_article(self, url): @@ -170,8 +169,10 @@ class FinancialTimes(BasicNewsRecipe): except: print "Retrying download..." count += 1 - tfile = PersistentTemporaryFile('_fa.html') - tfile.write(html) - tfile.close() - self.temp_files.append(tfile) - return tfile.name + self.temp_files.append(PersistentTemporaryFile('_fa.html')) + self.temp_files[-1].write(html) + self.temp_files[-1].close() + return self.temp_files[-1].name + + def cleanup(self): + self.browser.open('https://registration.ft.com/registration/login/logout?location=') diff --git a/recipes/nytimesbook.recipe b/recipes/nytimesbook.recipe index 686f30b69a..5388da9dcb 100644 --- a/recipes/nytimesbook.recipe +++ b/recipes/nytimesbook.recipe @@ -1,5 +1,4 @@ from calibre.web.feeds.news import BasicNewsRecipe -from calibre.ebooks.BeautifulSoup import BeautifulSoup class NewYorkTimesBookReview(BasicNewsRecipe): title = u'New York Times Book Review' @@ -7,50 +6,16 @@ class NewYorkTimesBookReview(BasicNewsRecipe): __author__ = 'Krittika Goyal' oldest_article = 8 #days max_articles_per_feed = 1000 - recursions = 2 + #recursions = 2 #encoding = 'latin1' + use_embedded_content = False + + no_stylesheets = True + auto_cleanup = True - remove_stylesheets = True - #remove_tags_before = dict(name='h1', attrs={'class':'heading'}) - remove_tags_after = dict(name='div', attrs={'id':'authorId'}) - remove_tags = [ - dict(name='iframe'), - dict(name=['div', 'a'], attrs={'class':['enlargeThis', 'jumpLink']}), - dict(name='div', attrs={'id':['sidebarArticles', 'toolsRight']}), - #dict(name='ul', attrs={'class':'article-tools'}), - #dict(name='ul', attrs={'class':'articleTools'}), - ] - match_regexps = [ - r'http://www.nytimes.com/.+pagewanted=[2-9]+' - ] feeds = [ -('New York Times Sunday Book Review', - 'http://feeds.nytimes.com/nyt/rss/SundayBookReview'), -] + ('New York Times Sunday Book Review', + 'http://feeds.nytimes.com/nyt/rss/SundayBookReview'), + ] - - def preprocess_html(self, soup): - story = soup.find(name='div', attrs={'id':'article'}) - #td = heading.findParent(name='td') - #td.extract() - soup = BeautifulSoup('t') - body = soup.find(name='body') - body.insert(0, story) - #for x in soup.findAll(name='p', text=lambda x:x and '-->' in x): - #p = x.findParent('p') - #if p is not None: - #p.extract() - return soup - - def postprocess_html(self, soup, first): - for div in soup.findAll(id='pageLinks'): - div.extract() - if not first: - h1 = soup.find('h1') - if h1 is not None: - h1.extract() - t = soup.find(attrs={'class':'timestamp'}) - if t is not None: - t.extract() - return soup diff --git a/src/calibre/constants.py b/src/calibre/constants.py index 6bc9995384..f098b77f9a 100644 --- a/src/calibre/constants.py +++ b/src/calibre/constants.py @@ -4,7 +4,7 @@ __license__ = 'GPL v3' __copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net' __docformat__ = 'restructuredtext en' __appname__ = u'calibre' -numeric_version = (0, 9, 3) +numeric_version = (0, 9, 4) __version__ = u'.'.join(map(unicode, numeric_version)) __author__ = u"Kovid Goyal " diff --git a/src/calibre/ebooks/oeb/transforms/flatcss.py b/src/calibre/ebooks/oeb/transforms/flatcss.py index cb5720c08a..6aae85d8d1 100644 --- a/src/calibre/ebooks/oeb/transforms/flatcss.py +++ b/src/calibre/ebooks/oeb/transforms/flatcss.py @@ -184,6 +184,11 @@ class CSSFlattener(object): faces = fontconfig.fonts_for_family(family) if not faces or not u'normal' in faces: msg = (u'No embeddable fonts found for family: %r'%self.opts.embed_font_family) + if faces: + msg = (u'The selected font %s has no Regular typeface, only' + ' %s faces, it cannot be used.')%( + self.opts.embed_font_family, + ', '.join(faces.iterkeys())) if failure_critical: raise ValueError(msg) self.oeb.log.warn(msg) diff --git a/src/calibre/gui2/dialogs/metadata_bulk.ui b/src/calibre/gui2/dialogs/metadata_bulk.ui index 2c4a409a22..917dfbb159 100644 --- a/src/calibre/gui2/dialogs/metadata_bulk.ui +++ b/src/calibre/gui2/dialogs/metadata_bulk.ui @@ -504,7 +504,11 @@ from the value in the box - When doing a same format to same format conversion, for e.g., EPUB to EPUB, calibre saves the original EPUB as ORIGINAL_EPUB. This option tells calibre to restore the EPUB from ORIGINAL_EPUB. Useful if you did a bulk conversion of a large number of books and something went wrong. + When doing a same format to same format conversion, +for e.g., EPUB to EPUB, calibre saves the original EPUB + as ORIGINAL_EPUB. This option tells calibre to restore + the EPUB from ORIGINAL_EPUB. Useful if you did a bulk + conversion of a large number of books and something went wrong. Restore pre conversion &originals, if available diff --git a/src/calibre/gui2/font_family_chooser.py b/src/calibre/gui2/font_family_chooser.py index b69d596d5f..5110e92d24 100644 --- a/src/calibre/gui2/font_family_chooser.py +++ b/src/calibre/gui2/font_family_chooser.py @@ -13,6 +13,7 @@ from PyQt4.Qt import (QFontInfo, QFontMetrics, Qt, QFont, QFontDatabase, QPen, QToolButton, QGridLayout, QListView, QWidget, QDialogButtonBox, QIcon, QHBoxLayout, QLabel, QModelIndex) +from calibre.gui2 import error_dialog from calibre.utils.icu import sort_key def writing_system_for_font(font): @@ -173,6 +174,20 @@ class FontFamilyDialog(QDialog): if idx == 0: return None return self.families[idx] + def accept(self): + ff = self.font_family + if ff: + from calibre.utils.fonts import fontconfig + faces = fontconfig.fonts_for_family(ff) or {} + faces = frozenset(faces.iterkeys()) + if 'normal' not in faces: + error_dialog(self, _('Not a useable font'), + _('The %s font family does not have a Regular typeface, so it' + ' cannot be used. It has only the "%s" face(s).')%( + ff, ', '.join(faces)), show=True) + return + QDialog.accept(self) + class FontFamilyChooser(QWidget): family_changed = pyqtSignal(object) diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index 05f5cdec74..d4cdf8cc1f 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -646,12 +646,13 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): spath = os.path.join(self.library_path, *current_path.split('/')) tpath = os.path.join(self.library_path, *path.split('/')) - wam = WindowsAtomicFolderMove(spath) if iswindows and current_path else None + source_ok = current_path and os.path.exists(spath) + wam = WindowsAtomicFolderMove(spath) if iswindows and source_ok else None try: if not os.path.exists(tpath): os.makedirs(tpath) - if current_path and os.path.exists(spath): # Migrate existing files + if source_ok: # Migrate existing files self.copy_cover_to(id, os.path.join(tpath, 'cover.jpg'), index_is_id=True, windows_atomic_move=wam, use_hardlink=True) @@ -669,7 +670,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): self.conn.commit() self.data.set(id, self.FIELD_MAP['path'], path, row_is_id=True) # Delete not needed directories - if current_path and os.path.exists(spath): + if source_ok: if not samefile(spath, tpath): if wam is not None: wam.delete_originals() @@ -1432,7 +1433,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): if use_hardlink: try: hardlink_file(path, dest) - return + return True except: pass with lopen(dest, 'wb') as d: diff --git a/src/calibre/translations/calibre.pot b/src/calibre/translations/calibre.pot index 799d7bc4fb..8648b192c0 100644 --- a/src/calibre/translations/calibre.pot +++ b/src/calibre/translations/calibre.pot @@ -4,9 +4,9 @@ # msgid "" msgstr "" -"Project-Id-Version: calibre 0.9.3\n" -"POT-Creation-Date: 2012-10-24 09:36+IST\n" -"PO-Revision-Date: 2012-10-24 09:36+IST\n" +"Project-Id-Version: calibre 0.9.4\n" +"POT-Creation-Date: 2012-10-26 09:57+IST\n" +"PO-Revision-Date: 2012-10-26 09:57+IST\n" "Last-Translator: Automatically generated\n" "Language-Team: LANGUAGE\n" "MIME-Version: 1.0\n" @@ -173,12 +173,12 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/library/database2.py:586 #: /home/kovid/work/calibre/src/calibre/library/database2.py:594 #: /home/kovid/work/calibre/src/calibre/library/database2.py:605 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:2273 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:2427 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:2857 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:3504 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:3506 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:3643 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:2274 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:2428 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:2858 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:3505 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:3507 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:3644 #: /home/kovid/work/calibre/src/calibre/library/server/content.py:250 #: /home/kovid/work/calibre/src/calibre/library/server/content.py:251 #: /home/kovid/work/calibre/src/calibre/library/server/mobile.py:247 @@ -882,26 +882,26 @@ 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:668 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:1061 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:1062 #: /home/kovid/work/calibre/src/calibre/utils/formatter_functions.py:887 #: /home/kovid/work/calibre/src/calibre/utils/formatter_functions.py:910 msgid "Yes" msgstr "" #: /home/kovid/work/calibre/src/calibre/db/fields.py:163 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:1216 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:1217 msgid "Main" msgstr "" #: /home/kovid/work/calibre/src/calibre/db/fields.py:165 #: /home/kovid/work/calibre/src/calibre/gui2/layout.py:77 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:1218 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:1219 msgid "Card A" msgstr "" #: /home/kovid/work/calibre/src/calibre/db/fields.py:167 #: /home/kovid/work/calibre/src/calibre/gui2/layout.py:79 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:1220 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:1221 msgid "Card B" msgstr "" @@ -1048,14 +1048,14 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/tag_browser/model.py:1199 #: /home/kovid/work/calibre/src/calibre/library/database2.py:371 #: /home/kovid/work/calibre/src/calibre/library/database2.py:384 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:3361 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:3362 #: /home/kovid/work/calibre/src/calibre/library/field_metadata.py:187 msgid "News" msgstr "" #: /home/kovid/work/calibre/src/calibre/devices/apple/driver.py:2770 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:3317 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:3335 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:3318 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:3336 msgid "Catalog" msgstr "" @@ -1099,10 +1099,10 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/devices/kobo/driver.py:128 #: /home/kovid/work/calibre/src/calibre/devices/kobo/driver.py:131 #: /home/kovid/work/calibre/src/calibre/devices/kobo/driver.py:348 -#: /home/kovid/work/calibre/src/calibre/devices/kobo/driver.py:1307 -#: /home/kovid/work/calibre/src/calibre/devices/kobo/driver.py:1311 #: /home/kovid/work/calibre/src/calibre/devices/kobo/driver.py:1315 -#: /home/kovid/work/calibre/src/calibre/devices/kobo/driver.py:1635 +#: /home/kovid/work/calibre/src/calibre/devices/kobo/driver.py:1319 +#: /home/kovid/work/calibre/src/calibre/devices/kobo/driver.py:1323 +#: /home/kovid/work/calibre/src/calibre/devices/kobo/driver.py:1642 #: /home/kovid/work/calibre/src/calibre/devices/prst1/driver.py:155 #: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:144 #: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:147 @@ -11001,6 +11001,10 @@ msgstr "" msgid "Choose &font family" msgstr "" +#: /home/kovid/work/calibre/src/calibre/gui2/font_family_chooser.py:196 +msgid "Clear the font family" +msgstr "" + #: /home/kovid/work/calibre/src/calibre/gui2/init.py:108 #: /home/kovid/work/calibre/src/calibre/gui2/preferences/look_feel_ui.py:296 msgid "Cover Browser" @@ -12216,7 +12220,7 @@ msgstr "" msgid "" "If set, this option will causes calibre to check if a file\n" " being auto-added is already in the calibre library.\n" -" If it is, a meesage will pop up asking you whether\n" +" If it is, a message will pop up asking you whether\n" " you want to add it anyway." msgstr "" @@ -15287,7 +15291,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:1106 +#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:1107 msgid "Remember last used window size" msgstr "" @@ -15854,35 +15858,35 @@ msgstr "" msgid "Loading ebook..." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:977 +#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:978 msgid "Could not open ebook" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:978 +#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:979 msgid "Unknown error" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:1093 +#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:1094 msgid "Options to control the ebook viewer" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:1100 +#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:1101 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:1103 +#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:1104 msgid "If specified, viewer window will try to open full screen when started." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:1108 +#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:1109 msgid "Print javascript alert and console messages to the console" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:1110 +#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:1111 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:1117 +#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:1118 msgid "" "%prog [options] file\n" "\n" @@ -17358,17 +17362,17 @@ msgstr "" msgid "creating custom column " msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/database2.py:3669 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:3670 #, python-format msgid "

Migrating old database to ebook library in %s

" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/database2.py:3698 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:3699 #, python-format msgid "Copying %s" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/database2.py:3715 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:3716 msgid "Compacting database" msgstr "" diff --git a/src/calibre/utils/filenames.py b/src/calibre/utils/filenames.py index b49c039ffe..5c31c92c33 100644 --- a/src/calibre/utils/filenames.py +++ b/src/calibre/utils/filenames.py @@ -314,6 +314,17 @@ class WindowsAtomicFolderMove(object): break f.write(raw) + def release_file(self, path): + key = None + for p, h in self.handle_map.iteritems(): + if samefile_windows(path, p): + key = (p, h) + break + if key is not None: + import win32file + win32file.CloseHandle(key[1]) + self.handle_map.pop(key[0]) + def close_handles(self): import win32file for h in self.handle_map.itervalues():