diff --git a/Changelog.yaml b/Changelog.yaml index d6204743f8..c4a8ffc2dd 100644 --- a/Changelog.yaml +++ b/Changelog.yaml @@ -4,6 +4,70 @@ # for important features/bug fixes. # Also, each release can have new and improved recipes. +- version: 0.6.54 + date: 2010-05-21 + + new features: + - title: "EPUB Output: Add option to toggle preserving the aspect ratio of the cover." + type: major + description: > + "By default calibre creates an SVG based cover that scales with the screen size of the reader used to view it. Previosuly this scaling + was limited to preserve the aspect ratio of the image. This would often result in white borders at the sides or top and bottom of the image. + No, by default, calibre will setup the cover to not preserve aspect ratio, doing away with the white borders. The downside is that if the + aspect ratio of the cover is very different from the reader, it will look distorted. The old behavior can be restored via + Preferences->Conversion->EPUB Output." + + - title: "Conversion pipeline: calibre will now automatically replace all ligatures in the input document." + type: major + description: > + "Conversion pipeline: calibre will now automatically replace all ligatures in the input document with the normal character + sequence they are meant to represent. This is because most readers lack the font support to display ligatures. + This can be turned off via an option under Look & Feel, in the Conversion settings." + + - title: "Support for the iPapyrus and Newsmy readers and the Sony Ericsson XPERIA X10" + + - title: "PDF Output: Set the first page to the cover." + tickets: [5581] + + bug fixes: + - title: "Conversion pipeline: Handle input documents with no text. Allows conversion of MOBI files tha are only a sequence of images." + tickets: [5554] + + - title: "Fix text justification control not working with translated version of calibre" + tickets: [5551] + + - title: "HTML Input: Encoding detection fixed for tags that have newlines in their content attributes" + tickets: [5567] + + - title: "EPUB Input: Handle malformed UUID in EPUB with obfuscated fonts." + tickets: [5552] + + - title: "Don't resort when editing columns in the main GUI" + + - title: "Fix regression in Kobo driver that caused it to only detect books in the root directory of the device" + + new recipes: + - title: La Stampa and Libero + author: Gabriele Marini + + - title: Der Tagesspiegel + author: ipaschke + + - title: EMG and Agro Gerilla + author: Darko Miletic + + - title: American Prospect, FactCheck and PolitiFact + author: Michael Heinz + + improved recipes: + - Times Online + - The Atlantic + - Il Messagero + - Leggo + - Instapaper + - New York Review of Books + - NIN Online + - version: 0.6.53 date: 2010-05-15 @@ -157,7 +221,7 @@ new features: - title: "Add merge book feature" type: major - desc: > + description: > "You can now merge multiple books into a single book, by clicking the arrow next to the edit meta information button. Meta information from the books will be merged as well as individual book files in different formats" diff --git a/resources/recipes/atlantic.recipe b/resources/recipes/atlantic.recipe index c6db016010..a41a931e37 100644 --- a/resources/recipes/atlantic.recipe +++ b/resources/recipes/atlantic.recipe @@ -5,7 +5,7 @@ __copyright__ = '2008, Kovid Goyal ' ''' theatlantic.com ''' -import string +import string, re from calibre.web.feeds.news import BasicNewsRecipe from calibre.ebooks.BeautifulSoup import Tag, NavigableString @@ -23,6 +23,8 @@ class TheAtlantic(BasicNewsRecipe): remove_tags = [dict(id=['header', 'printAds', 'pageControls'])] no_stylesheets = True + preprocess_regexps = [(re.compile(r'', re.DOTALL), lambda m: '')] + def print_version(self, url): return url.replace('/archive/', '/print/') diff --git a/resources/recipes/economist.recipe b/resources/recipes/economist.recipe index 35e06e65e6..4ae0bb8b05 100644 --- a/resources/recipes/economist.recipe +++ b/resources/recipes/economist.recipe @@ -22,7 +22,7 @@ class Economist(BasicNewsRecipe): ' Needs a subscription from ')+INDEX oldest_article = 7.0 - cover_url = 'http://www.economist.com/images/covers/currentcovereu_large.jpg' + cover_url = 'http://www.economist.com/images/covers/currentcoverus_large.jpg' remove_tags = [dict(name=['script', 'noscript', 'title', 'iframe', 'cf_floatingcontent']), dict(attrs={'class':['dblClkTrk']})] remove_tags_before = dict(name=lambda tag: tag.name=='title' and tag.parent.name=='body') diff --git a/resources/recipes/economist_free.recipe b/resources/recipes/economist_free.recipe index 32e108d2d6..cdcd457501 100644 --- a/resources/recipes/economist_free.recipe +++ b/resources/recipes/economist_free.recipe @@ -15,7 +15,7 @@ class Economist(BasicNewsRecipe): ' Much slower than the subscription based version.') oldest_article = 7.0 - cover_url = 'http://www.economist.com/images/covers/currentcovereu_large.jpg' + cover_url = 'http://www.economist.com/images/covers/currentcoverus_large.jpg' remove_tags = [dict(name=['script', 'noscript', 'title', 'iframe', 'cf_floatingcontent']), dict(attrs={'class':['dblClkTrk']})] remove_tags_before = dict(name=lambda tag: tag.name=='title' and tag.parent.name=='body') diff --git a/resources/recipes/tagesspiegel.recipe b/resources/recipes/tagesspiegel.recipe new file mode 100644 index 0000000000..e5d2600ae0 --- /dev/null +++ b/resources/recipes/tagesspiegel.recipe @@ -0,0 +1,86 @@ +__license__ = 'GPL v3' +__copyright__ = '2010 Ingo Paschke ' + +''' +Fetch Tagesspiegel. +''' +import string, re +from calibre import strftime +from calibre.web.feeds.news import BasicNewsRecipe + +class TagesspiegelRSS(BasicNewsRecipe): + title = u'Der Tagesspiegel' + __author__ = 'ipaschke' + language = 'de' + oldest_article = 7 + max_articles_per_feed = 100 + + extra_css = ''' + .hcf-overline{color:#990000; font-family:Arial,Helvetica,sans-serif;font-size:xx-small;display:block} + .hcf-teaser{font-family:Verdana,Arial,Helvetica;font-size:x-small;margin-top:0} + h1{font-family:Arial,Helvetica,sans-serif;font-size:large;clear:right;} + .hcf-caption{color:#666666; font-family:Arial,Helvetica,sans-serif;font-size:xx-small;} + .hcf-copyright{color:#666666; font-family:Arial,Helvetica,sans-serif;font-size:xx-small;} + .hcf-article{font-family:Arial,Helvetica;font-size:x-small} + .quote{font-family:Georgia,Palatino,Palatino Linotype,FreeSerif,serif;font-size:x-small} + .quote .cite{font-family:Georgia,Palatino,Palatino Linotype,FreeSerif,serif;font-size:xx-small} + .hcf-inline-left{float:left;margin-right:15px;position:relative;} + .hcf-inline-right{float:right;margin-right:15px;position:relative;} + .hcf-smart-box{font-family: Arial, Helvetica, sans-serif; font-size: xx-small; margin: 0px 15px 8px 0px; width: 300px;} + ''' + + no_stylesheets = True + no_javascript = True + remove_empty_feeds = True + encoding = 'utf-8' + + keep_only_tags = dict(name='div', attrs={'class':["hcf-article"]}) + remove_tags = [ + dict(name='link'), dict(name='iframe'),dict(name='style'),dict(name='meta'),dict(name='button'), + dict(name='div', attrs={'class':["hcf-jump-to-comments","hcf-clear","hcf-magnify hcf-media-control"] }), + dict(name='span', attrs={'class':["hcf-mainsearch",] }), + dict(name='ul', attrs={'class':["hcf-tools"] }), + ] + + def parse_index(self): + soup = self.index_to_soup('http://www.tagesspiegel.de/zeitung/') + + def feed_title(div): + return ''.join(div.findAll(text=True, recursive=False)).strip() + + articles = {} + key = None + ans = [] + + for div in soup.findAll(True, attrs={'class':['hcf-teaser', 'hcf-header', 'story headline']}): + + if div['class'] == 'hcf-header': + key = string.capwords(feed_title(div.em.a)) + articles[key] = [] + ans.append(key) + + elif div['class'] == 'hcf-teaser' and getattr(div.contents[0],'name','') == 'h2': + a = div.find('a', href=True) + if not a: + continue + url = 'http://www.tagesspiegel.de' + a['href'] + title = self.tag_to_string(a, use_alt=True).strip() + description = '' + pubdate = strftime('%a, %d %b') + summary = div.find('p', attrs={'class':'hcf-teaser'}) + if summary: + description = self.tag_to_string(summary, use_alt=False) + + feed = key if key is not None else 'Uncategorized' + if not articles.has_key(feed): + articles[feed] = [] + if not 'podcasts' in url: + articles[feed].append( + dict(title=title, url=url, date=pubdate, + description=re.sub('mehr$', '', description), + content='')) + + ans = [(key, articles[key]) for key in ans if articles.has_key(key)] + + return ans + diff --git a/resources/recipes/times_online.recipe b/resources/recipes/times_online.recipe index 98e96552ce..a57749c79d 100644 --- a/resources/recipes/times_online.recipe +++ b/resources/recipes/times_online.recipe @@ -5,6 +5,7 @@ __copyright__ = '2008-2009, Darko Miletic ' ''' timesonline.co.uk ''' +import re from calibre.web.feeds.news import BasicNewsRecipe from calibre.ebooks.BeautifulSoup import Tag @@ -26,6 +27,8 @@ class Timesonline(BasicNewsRecipe): recursions = 9 match_regexps = [r'http://www.timesonline.co.uk/.*page=[2-9]'] + preprocess_regexps = [(re.compile(r'', re.DOTALL), lambda m: '')] + keep_only_tags = [ dict(name='div', attrs= {'id':['region-column1and2-layout2']}), {'class' : ['subheading']}, @@ -76,8 +79,7 @@ class Timesonline(BasicNewsRecipe): soup = self.index_to_soup(index) link_item = soup.find(name = 'div',attrs ={'class': "float-left margin-right-15"}) if link_item: - cover_url = 'http://www.timesonline.co.uk' + link_item.img['src'] - print cover_url + cover_url = link_item.img['src'] return cover_url def get_article_url(self, article): @@ -85,9 +87,9 @@ class Timesonline(BasicNewsRecipe): def preprocess_html(self, soup): - soup.html['xml:lang'] = self.lang - soup.html['lang'] = self.lang - mlang = Tag(soup,'meta',[("http-equiv","Content-Language"),("content",self.lang)]) + soup.html['xml:lang'] = self.language + soup.html['lang'] = self.language + mlang = Tag(soup,'meta',[("http-equiv","Content-Language"),("content",self.language)]) mcharset = Tag(soup,'meta',[("http-equiv","Content-Type"),("content","text/html; charset=ISO-8859-1")]) soup.head.insert(0,mlang) soup.head.insert(1,mcharset) diff --git a/src/calibre/ebooks/epub/output.py b/src/calibre/ebooks/epub/output.py index 129a63ef3c..71d9d8b423 100644 --- a/src/calibre/ebooks/epub/output.py +++ b/src/calibre/ebooks/epub/output.py @@ -46,8 +46,155 @@ block_level_tags = ( 'ul', ) +class CoverManager(object): -class EPUBOutput(OutputFormatPlugin): + ''' + Manage the cover in the output document. Requires the opts object to have + the attributes: + + no_svg_cover + no_default_epub_cover + preserve_cover_aspect_ratio + ''' + + NONSVG_TITLEPAGE_COVER = '''\ + + + + + Cover + + + +
+ cover +
+ + + ''' + + TITLEPAGE_COVER = '''\ + + + + + Cover + + + + + + + + +''' + + def default_cover(self): + ''' + Create a generic cover for books that dont have a cover + ''' + from calibre.utils.pil_draw import draw_centered_text + from calibre.ebooks.metadata import authors_to_string + if self.opts.no_default_epub_cover: + return None + self.log('Generating default cover') + m = self.oeb.metadata + title = unicode(m.title[0]) + authors = [unicode(x) for x in m.creator if x.role == 'aut'] + + import cStringIO + cover_file = cStringIO.StringIO() + try: + try: + from PIL import Image, ImageDraw, ImageFont + Image, ImageDraw, ImageFont + except ImportError: + import Image, ImageDraw, ImageFont + font_path = P('fonts/liberation/LiberationSerif-Bold.ttf') + app = '['+__appname__ +' '+__version__+']' + + COVER_WIDTH, COVER_HEIGHT = 590, 750 + img = Image.new('RGB', (COVER_WIDTH, COVER_HEIGHT), 'white') + draw = ImageDraw.Draw(img) + # Title + font = ImageFont.truetype(font_path, 44) + bottom = draw_centered_text(img, draw, font, title, 15, ysep=9) + # Authors + bottom += 14 + font = ImageFont.truetype(font_path, 32) + authors = authors_to_string(authors) + bottom = draw_centered_text(img, draw, font, authors, bottom, ysep=7) + # Vanity + font = ImageFont.truetype(font_path, 28) + width, height = draw.textsize(app, font=font) + left = max(int((COVER_WIDTH - width)/2.), 0) + top = COVER_HEIGHT - height - 15 + draw.text((left, top), app, fill=(0,0,0), font=font) + # Logo + logo = Image.open(I('library.png'), 'r') + width, height = logo.size + left = max(int((COVER_WIDTH - width)/2.), 0) + top = max(int((COVER_HEIGHT - height)/2.), 0) + img.paste(logo, (left, max(bottom, top))) + img = img.convert('RGB').convert('P', palette=Image.ADAPTIVE) + + img.convert('RGB').save(cover_file, 'JPEG') + cover_file.flush() + id, href = self.oeb.manifest.generate('cover_image', 'cover_image.jpg') + item = self.oeb.manifest.add(id, href, guess_type('t.jpg')[0], + data=cover_file.getvalue()) + m.clear('cover') + m.add('cover', item.id) + + return item.href + except: + self.log.exception('Failed to generate default cover') + return None + + + def insert_cover(self): + from calibre.ebooks.oeb.base import urldefrag + from calibre import guess_type + g, m = self.oeb.guide, self.oeb.manifest + item = None + ar = 'xMidYMid meet' if self.opts.preserve_cover_aspect_ratio else \ + 'none' + svg_template = self.TITLEPAGE_COVER.replace('__ar__', ar) + if 'titlepage' not in g: + if 'cover' in g: + href = g['cover'].href + else: + href = self.default_cover() + if href is not None: + templ = self.NONSVG_TITLEPAGE_COVER if self.opts.no_svg_cover \ + else svg_template + tp = templ%unquote(href) + id, href = m.generate('titlepage', 'titlepage.xhtml') + item = m.add(id, href, guess_type('t.xhtml')[0], + data=etree.fromstring(tp)) + else: + item = self.oeb.manifest.hrefs[ + urldefrag(self.oeb.guide['titlepage'].href)[0]] + if item is not None: + self.oeb.spine.insert(0, item, True) + if 'cover' not in self.oeb.guide.refs: + self.oeb.guide.add('cover', 'Title Page', 'a') + self.oeb.guide.refs['cover'].href = item.href + if 'titlepage' in self.oeb.guide.refs: + self.oeb.guide.refs['titlepage'].href = item.href + + +class EPUBOutput(OutputFormatPlugin, CoverManager): name = 'EPUB Output' author = 'Kovid Goyal' @@ -92,51 +239,21 @@ class EPUBOutput(OutputFormatPlugin): 'as a blank page.') ), + OptionRecommendation(name='preserve_cover_aspect_ratio', + recommended_value=False, help=_( + 'When using an SVG cover, this option will cause the cover to scale ' + 'to cover the available screen area, but still preserve its aspect ratio ' + '(ratio of width to height). That means there may be white borders ' + 'at the sides or top and bottom of the image, but the image will ' + 'never be distorted. Without this option the image may be slightly ' + 'distorted, but there will be no borders.' + ) + ), + ]) recommendations = set([('pretty_print', True, OptionRecommendation.HIGH)]) - NONSVG_TITLEPAGE_COVER = '''\ - - - - - Cover - - - -
- cover -
- - - ''' - - TITLEPAGE_COVER = '''\ - - - - - Cover - - - - - - - - -''' def workaround_webkit_quirks(self): from calibre.ebooks.oeb.base import XPath @@ -259,97 +376,6 @@ class EPUBOutput(OutputFormatPlugin): ans += '\n' return ans - def default_cover(self): - ''' - Create a generic cover for books that dont have a cover - ''' - from calibre.utils.pil_draw import draw_centered_text - from calibre.ebooks.metadata import authors_to_string - if self.opts.no_default_epub_cover: - return None - self.log('Generating default cover') - m = self.oeb.metadata - title = unicode(m.title[0]) - authors = [unicode(x) for x in m.creator if x.role == 'aut'] - - import cStringIO - cover_file = cStringIO.StringIO() - try: - try: - from PIL import Image, ImageDraw, ImageFont - Image, ImageDraw, ImageFont - except ImportError: - import Image, ImageDraw, ImageFont - font_path = P('fonts/liberation/LiberationSerif-Bold.ttf') - app = '['+__appname__ +' '+__version__+']' - - COVER_WIDTH, COVER_HEIGHT = 590, 750 - img = Image.new('RGB', (COVER_WIDTH, COVER_HEIGHT), 'white') - draw = ImageDraw.Draw(img) - # Title - font = ImageFont.truetype(font_path, 44) - bottom = draw_centered_text(img, draw, font, title, 15, ysep=9) - # Authors - bottom += 14 - font = ImageFont.truetype(font_path, 32) - authors = authors_to_string(authors) - bottom = draw_centered_text(img, draw, font, authors, bottom, ysep=7) - # Vanity - font = ImageFont.truetype(font_path, 28) - width, height = draw.textsize(app, font=font) - left = max(int((COVER_WIDTH - width)/2.), 0) - top = COVER_HEIGHT - height - 15 - draw.text((left, top), app, fill=(0,0,0), font=font) - # Logo - logo = Image.open(I('library.png'), 'r') - width, height = logo.size - left = max(int((COVER_WIDTH - width)/2.), 0) - top = max(int((COVER_HEIGHT - height)/2.), 0) - img.paste(logo, (left, max(bottom, top))) - img = img.convert('RGB').convert('P', palette=Image.ADAPTIVE) - - img.convert('RGB').save(cover_file, 'JPEG') - cover_file.flush() - id, href = self.oeb.manifest.generate('cover_image', 'cover_image.jpg') - item = self.oeb.manifest.add(id, href, guess_type('t.jpg')[0], - data=cover_file.getvalue()) - m.clear('cover') - m.add('cover', item.id) - - return item.href - except: - self.log.exception('Failed to generate default cover') - return None - - - def insert_cover(self): - from calibre.ebooks.oeb.base import urldefrag - from calibre import guess_type - g, m = self.oeb.guide, self.oeb.manifest - item = None - if 'titlepage' not in g: - if 'cover' in g: - href = g['cover'].href - else: - href = self.default_cover() - if href is not None: - templ = self.NONSVG_TITLEPAGE_COVER if self.opts.no_svg_cover \ - else self.TITLEPAGE_COVER - tp = templ%unquote(href) - id, href = m.generate('titlepage', 'titlepage.xhtml') - item = m.add(id, href, guess_type('t.xhtml')[0], - data=etree.fromstring(tp)) - else: - item = self.oeb.manifest.hrefs[ - urldefrag(self.oeb.guide['titlepage'].href)[0]] - if item is not None: - self.oeb.spine.insert(0, item, True) - if 'cover' not in self.oeb.guide.refs: - self.oeb.guide.add('cover', 'Title Page', 'a') - self.oeb.guide.refs['cover'].href = item.href - if 'titlepage' in self.oeb.guide.refs: - self.oeb.guide.refs['titlepage'].href = item.href - def condense_ncx(self, ncx_path): if not self.opts.pretty_print: tree = etree.parse(ncx_path) diff --git a/src/calibre/ebooks/metadata/book/__init__.py b/src/calibre/ebooks/metadata/book/__init__.py new file mode 100644 index 0000000000..9a44a36489 --- /dev/null +++ b/src/calibre/ebooks/metadata/book/__init__.py @@ -0,0 +1,111 @@ +#!/usr/bin/env python +# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai + +__license__ = 'GPL v3' +__copyright__ = '2010, Kovid Goyal ' +__docformat__ = 'restructuredtext en' + +''' +All fields must have a NULL value represented as None for simple types, +an empty list/dictionary for complex types and (None, None) for cover_data +''' + +SOCIAL_METADATA_FIELDS = frozenset([ + 'tags', # Ordered list + # A floating point number between 0 and 10 + 'rating', + # A simple HTML enabled string + 'comments', + # A simple string + 'series', + # A floating point number + 'series_index', + # Of the form { scheme1:value1, scheme2:value2} + # For example: {'isbn':'123456789', 'doi':'xxxx', ... } + 'classifiers', + 'isbn', # Pseudo field for convenience, should get/set isbn classifier + +]) + +PUBLICATION_METADATA_FIELDS = frozenset([ + # title must never be None. Should be _('Unknown') + 'title', + # Pseudo field that can be set, but if not set is auto generated + # from title and languages + 'title_sort', + # Ordered list of authors. Must never be None, can be [_('Unknown')] + 'authors', + # Pseudo field that can be set, but if not set is auto generated + # from authors and languages + 'author_sort', + 'book_producer', + # Dates and times must be timezone aware + 'timestamp', + 'pubdate', + 'rights', + # So far only known publication type is periodical:calibre + # If None, means book + 'publication_type', + # A UUID usually of type 4 + 'uuid', + 'languages', # ordered list + # Simple string, no special semantics + 'publisher', + # Absolute path to image file encoded in filesystem_encoding + 'cover', + # Of the form (format, data) where format is, for e.g. 'jpeg', 'png', 'gif'... + 'cover_data', + # Either thumbnail data, or an object with the attribute + # image_path which is the path to an image file, encoded + # in filesystem_encoding + 'thumbnail', + ]) + +BOOK_STRUCTURE_FIELDS = frozenset([ + # These are used by code, Null values are None. + 'toc', 'spine', 'guide', 'manifest', + ]) + +USER_METADATA_FIELDS = frozenset([ + # A dict of a form to be specified + 'user_metadata', +]) + +DEVICE_METADATA_FIELDS = frozenset([ + # Ordered list of strings + 'device_collections', + 'lpath', # Unicode, / separated + # In bytes + 'size', + # Mimetype of the book file being represented + 'mime', +]) + +CALIBRE_METADATA_FIELDS = frozenset([ + # An application id + # Semantics to be defined. Is it a db key? a db name + key? A uuid? + 'application_id', + ] +) + +RESERVED_METADATA_FIELDS = SOCIAL_METADATA_FIELDS.union( + PUBLICATION_METADATA_FIELDS).union( + BOOK_STRUCTURE_FIELDS).union( + USER_METADATA_FIELDS).union( + DEVICE_METADATA_FIELDS).union( + CALIBRE_METADATA_FIELDS) + +assert len(RESERVED_METADATA_FIELDS) == sum(map(len, ( + SOCIAL_METADATA_FIELDS, PUBLICATION_METADATA_FIELDS, + BOOK_STRUCTURE_FIELDS, USER_METADATA_FIELDS, + DEVICE_METADATA_FIELDS, CALIBRE_METADATA_FIELDS, + ))) + +SERIALIZABLE_FIELDS = SOCIAL_METADATA_FIELDS.union( + USER_METADATA_FIELDS).union( + PUBLICATION_METADATA_FIELDS).union( + CALIBRE_METADATA_FIELDS).union( + frozenset(['lpath'])) # I don't think we need device_collections + +# Serialization of covers/thumbnails will have to be handled carefully, maybe +# as an option to the serializer class diff --git a/src/calibre/ebooks/metadata/book/base.py b/src/calibre/ebooks/metadata/book/base.py new file mode 100644 index 0000000000..bf653b38bb --- /dev/null +++ b/src/calibre/ebooks/metadata/book/base.py @@ -0,0 +1,129 @@ +#!/usr/bin/env python +# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai + +__license__ = 'GPL v3' +__copyright__ = '2010, Kovid Goyal ' +__docformat__ = 'restructuredtext en' + +import copy + +from calibre.ebooks.metadata.book import RESERVED_METADATA_FIELDS + +NULL_VALUES = { + 'user_metadata': {}, + 'cover_data' : (None, None), + 'tags' : [], + 'classifiers' : {}, + 'languages' : [], + 'device_collections': [], + 'authors' : [_('Unknown')], + 'title' : _('Unknown'), +} + +class Metadata(object): + + ''' + This class must expose a superset of the API of MetaInformation in terms + of attribute access and methods. Only the __init__ method is different. + MetaInformation will simply become a function that creates and fills in + the attributes of this class. + + Please keep the method based API of this class to a minimum. Every method + becomes a reserved field name. + ''' + + def __init__(self): + object.__setattr__(self, '_data', copy.deepcopy(NULL_VALUES)) + + def __getattribute__(self, field): + _data = object.__getattribute__(self, '_data') + if field in RESERVED_METADATA_FIELDS: + return _data.get(field, None) + try: + return object.__getattribute__(self, field) + except AttributeError: + pass + if field in _data['user_metadata'].iterkeys(): + # TODO: getting user metadata values + pass + raise AttributeError( + 'Metadata object has no attribute named: '+ repr(field)) + + + def __setattr__(self, field, val): + _data = object.__getattribute__(self, '_data') + if field in RESERVED_METADATA_FIELDS: + if field != 'user_metadata': + if not val: + val = NULL_VALUES[field] + _data[field] = val + else: + raise AttributeError('You cannot set user_metadata directly.') + elif field in _data['user_metadata'].iterkeys(): + # TODO: Setting custom column values + pass + else: + # You are allowed to stick arbitrary attributes onto this object as + # long as they dont conflict with global or user metadata names + # Don't abuse this privilege + self.__dict__[field] = val + + @property + def reserved_names(self): + 'The set of names you cannot use for your own purposes on this object' + _data = object.__getattribute__(self, '_data') + return frozenset(RESERVED_FIELD_NAMES).union(frozenset( + _data['user_metadata'].iterkeys())) + + @property + def user_metadata_names(self): + 'The set of user metadata names this object knows about' + _data = object.__getattribute__(self, '_data') + return frozenset(_data['user_metadata'].iterkeys()) + + # Old MetaInformation API {{{ + def copy(self): + pass + + def print_all_attributes(self): + pass + + def smart_update(self, other): + pass + + def format_series_index(self): + pass + + def authors_from_string(self, raw): + pass + + def format_authors(self): + pass + + def format_tags(self): + pass + + def format_rating(self): + return unicode(self.rating) + + def __unicode__(self): + pass + + def to_html(self): + pass + + def __str__(self): + return self.__unicode__().encode('utf-8') + + def __nonzero__(self): + return True + + # }}} + +_m = Metadata() +RESERVED_FIELD_NAMES = \ + frozenset(_m.__dict__.iterkeys()).union( # _data + RESERVED_METADATA_FIELDS).union( + frozenset(Metadata.__dict__.iterkeys())) # methods defined in Metadata +del _m + diff --git a/src/calibre/ebooks/metadata/fetch.py b/src/calibre/ebooks/metadata/fetch.py index 8907a0e34b..a7fd76c661 100644 --- a/src/calibre/ebooks/metadata/fetch.py +++ b/src/calibre/ebooks/metadata/fetch.py @@ -9,7 +9,6 @@ from threading import Thread from calibre import prints from calibre.utils.config import OptionParser from calibre.utils.logging import default_log -from calibre.ebooks.metadata import MetaInformation from calibre.customize import Plugin metadata_config = None @@ -53,7 +52,7 @@ class MetadataSource(Plugin): if self.results: c = self.config_store().get(self.name, {}) res = self.results - if isinstance(res, MetaInformation): + if hasattr(res, 'authors'): res = [res] for mi in res: if not c.get('rating', True): diff --git a/src/calibre/ebooks/metadata/odt.py b/src/calibre/ebooks/metadata/odt.py old mode 100755 new mode 100644 diff --git a/src/calibre/ebooks/metadata/opf2.py b/src/calibre/ebooks/metadata/opf2.py index e429574e57..2f61b5b8fe 100644 --- a/src/calibre/ebooks/metadata/opf2.py +++ b/src/calibre/ebooks/metadata/opf2.py @@ -669,10 +669,19 @@ class OPF(object): remove = list(self.authors_path(self.metadata)) for elem in remove: elem.getparent().remove(elem) + elems = [] for author in val: attrib = {'{%s}role'%self.NAMESPACES['opf']: 'aut'} elem = self.create_metadata_element('creator', attrib=attrib) self.set_text(elem, author.strip()) + # Ensure new author element is at the top of the list + # for broken implementations that always use the first + # element with no attention to the role + elems.append(elem) + for elem in reversed(elems): + parent = elem.getparent() + parent.remove(elem) + parent.insert(0, elem) return property(fget=fget, fset=fset) diff --git a/src/calibre/ebooks/pdf/output.py b/src/calibre/ebooks/pdf/output.py index b2d649c2cf..e302f67441 100644 --- a/src/calibre/ebooks/pdf/output.py +++ b/src/calibre/ebooks/pdf/output.py @@ -15,11 +15,39 @@ from calibre.customize.conversion import OutputFormatPlugin, \ OptionRecommendation from calibre.ebooks.metadata.opf2 import OPF from calibre.ptempfile import TemporaryDirectory -from calibre.ebooks.pdf.writer import PDFWriter, ImagePDFWriter, PDFMetadata +from calibre.ebooks.pdf.writer import PDFWriter, ImagePDFWriter, PDFMetadata, \ + get_pdf_page_size from calibre.ebooks.pdf.pageoptions import UNITS, PAPER_SIZES, \ ORIENTATIONS +from calibre.ebooks.epub.output import CoverManager -class PDFOutput(OutputFormatPlugin): +class CoverManagerPDF(CoverManager): + + def setup_cover(self, opts): + width, height = get_pdf_page_size(opts) + factor = opts.output_profile.dpi + self.NONSVG_TITLEPAGE_COVER = '''\ + + + + + Cover + + + +
+ cover +
+ + + '''%(int(width*factor), int(height*factor)-5) + + +class PDFOutput(OutputFormatPlugin, CoverManagerPDF): name = 'PDF Output' author = 'John Schember' @@ -47,6 +75,7 @@ class PDFOutput(OutputFormatPlugin): ]) def convert(self, oeb_book, output_path, input_plugin, opts, log): + self.oeb = oeb_book self.input_plugin, self.opts, self.log = input_plugin, opts, log self.output_path = output_path self.metadata = oeb_book.metadata @@ -63,6 +92,10 @@ class PDFOutput(OutputFormatPlugin): def convert_text(self, oeb_book): self.log.debug('Serializing oeb input to disk for processing...') + self.opts.no_svg_cover = True + self.opts.no_default_epub_cover = True + self.setup_cover(self.opts) + self.insert_cover() with TemporaryDirectory('_pdf_out') as oeb_dir: from calibre.customize.ui import plugin_for_output_format oeb_output = plugin_for_output_format('oeb') diff --git a/src/calibre/ebooks/pdf/writer.py b/src/calibre/ebooks/pdf/writer.py index 9b5094ac95..22e653f275 100644 --- a/src/calibre/ebooks/pdf/writer.py +++ b/src/calibre/ebooks/pdf/writer.py @@ -18,11 +18,70 @@ from calibre.ebooks.metadata import authors_to_string from PyQt4 import QtCore from PyQt4.Qt import QUrl, QEventLoop, SIGNAL, QObject, \ - QPrinter, QMetaObject, QSizeF, Qt + QPrinter, QMetaObject, QSizeF, Qt, QPainter from PyQt4.QtWebKit import QWebView from pyPdf import PdfFileWriter, PdfFileReader +def get_custom_size(opts): + custom_size = None + if opts.custom_size != None: + width, sep, height = opts.custom_size.partition('x') + if height != '': + try: + width = int(width) + height = int(height) + custom_size = (width, height) + except: + custom_size = None + return custom_size + +def get_pdf_page_size(opts): + from calibre.gui2 import is_ok_to_use_qt + if not is_ok_to_use_qt(): + raise Exception('Not OK to use Qt') + + printer = QPrinter(QPrinter.HighResolution) + custom_size = get_custom_size(opts) + + if opts.output_profile.short_name == 'default': + if custom_size is None: + printer.setPaperSize(paper_size(opts.paper_size)) + else: + printer.setPaperSize(QSizeF(custom_size[0], custom_size[1]), unit(opts.unit)) + else: + printer.setPaperSize(QSizeF(opts.output_profile.width / opts.output_profile.dpi, + opts.output_profile.height / opts.output_profile.dpi), QPrinter.Inch) + + printer.setPageMargins(0, 0, 0, 0, QPrinter.Point) + printer.setOrientation(orientation(opts.orientation)) + printer.setOutputFormat(QPrinter.PdfFormat) + + size = printer.paperSize(QPrinter.Millimeter) + + return size.width() / 10, size.height() / 10 + +def get_imagepdf_page_size(opts): + printer = QPrinter(QPrinter.HighResolution) + custom_size = get_custom_size(opts) + + if opts.output_profile.short_name == 'default': + if custom_size == None: + printer.setPaperSize(paper_size(opts.paper_size)) + else: + printer.setPaperSize(QSizeF(custom_size[0], custom_size[1]), unit(opts.unit)) + else: + printer.setPaperSize(QSizeF(opts.output_profile.comic_screen_size[0] / opts.output_profile.dpi, + opts.output_profile.comic_screen_size[1] / opts.output_profile.dpi), QPrinter.Inch) + + printer.setPageMargins(0, 0, 0, 0, QPrinter.Point) + printer.setOrientation(orientation(opts.orientation)) + printer.setOutputFormat(QPrinter.PdfFormat) + + size = printer.paperSize(QPrinter.Millimeter) + + return size.width() / 10, size.height() / 10 + class PDFMetadata(object): def __init__(self, oeb_metadata=None): self.title = _('Unknown') @@ -36,6 +95,7 @@ class PDFMetadata(object): class PDFWriter(QObject): + def __init__(self, opts, log): from calibre.gui2 import is_ok_to_use_qt if not is_ok_to_use_qt(): @@ -46,25 +106,15 @@ class PDFWriter(QObject): self.loop = QEventLoop() self.view = QWebView() + self.view.setRenderHints(QPainter.Antialiasing|QPainter.TextAntialiasing|QPainter.SmoothPixmapTransform) self.connect(self.view, SIGNAL('loadFinished(bool)'), self._render_html) self.render_queue = [] self.combine_queue = [] self.tmp_path = PersistentTemporaryDirectory('_pdf_output_parts') - self.custom_size = None - if opts.custom_size != None: - width, sep, height = opts.custom_size.partition('x') - if height != '': - try: - width = int(width) - height = int(height) - self.custom_size = (width, height) - except: - self.custom_size = None - self.opts = opts - self.size = self._size() + self.size = get_pdf_page_size(opts) def dump(self, items, out_stream, pdf_metadata): self.metadata = pdf_metadata @@ -77,27 +127,6 @@ class PDFWriter(QObject): QMetaObject.invokeMethod(self, "_render_book", Qt.QueuedConnection) self.loop.exec_() - def _size(self): - ''' - The size of a pdf page in cm. - ''' - printer = QPrinter(QPrinter.HighResolution) - - if self.opts.output_profile.short_name == 'default': - if self.custom_size == None: - printer.setPaperSize(paper_size(self.opts.paper_size)) - else: - printer.setPaperSize(QSizeF(self.custom_size[0], self.custom_size[1]), unit(self.opts.unit)) - else: - printer.setPaperSize(QSizeF(self.opts.output_profile.width / self.opts.output_profile.dpi, self.opts.output_profile.height / self.opts.output_profile.dpi), QPrinter.Inch) - - printer.setPageMargins(0, 0, 0, 0, QPrinter.Point) - printer.setOrientation(orientation(self.opts.orientation)) - printer.setOutputFormat(QPrinter.PdfFormat) - - size = printer.paperSize(QPrinter.Millimeter) - - return size.width() / 10, size.height() / 10 @QtCore.pyqtSignature('_render_book()') def _render_book(self): @@ -151,6 +180,10 @@ class PDFWriter(QObject): class ImagePDFWriter(PDFWriter): + def __init__(self, opts, log): + PDFWriter.__init__(self, opts, log) + self.size = get_imagepdf_page_size(opts) + def _render_next(self): item = str(self.render_queue.pop(0)) self.combine_queue.append(os.path.join(self.tmp_path, '%i.pdf' % (len(self.combine_queue) + 1))) @@ -163,22 +196,4 @@ class ImagePDFWriter(PDFWriter): self.view.setHtml(html) - def _size(self): - printer = QPrinter(QPrinter.HighResolution) - - if self.opts.output_profile.short_name == 'default': - if self.custom_size == None: - printer.setPaperSize(paper_size(self.opts.paper_size)) - else: - printer.setPaperSize(QSizeF(self.custom_size[0], self.custom_size[1]), unit(self.opts.unit)) - else: - printer.setPaperSize(QSizeF(self.opts.output_profile.comic_screen_size[0] / self.opts.output_profile.dpi, self.opts.output_profile.comic_screen_size[1] / self.opts.output_profile.dpi), QPrinter.Inch) - - printer.setPageMargins(0, 0, 0, 0, QPrinter.Point) - printer.setOrientation(orientation(self.opts.orientation)) - printer.setOutputFormat(QPrinter.PdfFormat) - - size = printer.paperSize(QPrinter.Millimeter) - - return size.width() / 10, size.height() / 10 diff --git a/src/calibre/gui2/convert/epub_output.py b/src/calibre/gui2/convert/epub_output.py index 57027d9315..8130b00273 100644 --- a/src/calibre/gui2/convert/epub_output.py +++ b/src/calibre/gui2/convert/epub_output.py @@ -18,8 +18,11 @@ class PluginWidget(Widget, Ui_Form): def __init__(self, parent, get_option, get_help, db=None, book_id=None): Widget.__init__(self, parent, 'epub_output', ['dont_split_on_page_breaks', 'flow_size', - 'no_default_epub_cover', 'no_svg_cover'] + 'no_default_epub_cover', 'no_svg_cover', + 'preserve_cover_aspect_ratio',] ) + for i in range(2): + self.opt_no_svg_cover.toggle() self.db, self.book_id = db, book_id self.initialize_options(get_option, get_help, db, book_id) diff --git a/src/calibre/gui2/convert/epub_output.ui b/src/calibre/gui2/convert/epub_output.ui index 7f92ec3087..abca2405e8 100644 --- a/src/calibre/gui2/convert/epub_output.ui +++ b/src/calibre/gui2/convert/epub_output.ui @@ -14,13 +14,34 @@ Form - + Do not &split on page breaks + + + + No default &cover + + + + + + + No &SVG cover + + + + + + + Preserve cover &aspect ratio + + + @@ -60,22 +81,25 @@ - - - - No default &cover - - - - - - - No &SVG cover - - - - + + + opt_no_svg_cover + toggled(bool) + opt_preserve_cover_aspect_ratio + setDisabled(bool) + + + 81 + 73 + + + 237 + 68 + + + + diff --git a/src/calibre/gui2/dialogs/config/create_custom_column.py b/src/calibre/gui2/dialogs/config/create_custom_column.py index 296a868fbf..357e1e2ad8 100644 --- a/src/calibre/gui2/dialogs/config/create_custom_column.py +++ b/src/calibre/gui2/dialogs/config/create_custom_column.py @@ -8,9 +8,9 @@ from functools import partial from PyQt4.QtCore import SIGNAL from PyQt4.Qt import QDialog, Qt, QListWidgetItem, QVariant -from calibre.devices.metadata_serializer import metadata_serializer from calibre.gui2.dialogs.config.create_custom_column_ui import Ui_QCreateCustomColumn from calibre.gui2 import error_dialog +from calibre.ebooks.metadata.book.base import RESERVED_FIELD_NAMES class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn): @@ -103,7 +103,7 @@ class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn): return self.simple_error('', _('No lookup name was provided')) if not col_heading: return self.simple_error('', _('No column heading was provided')) - if col in metadata_serializer.SERIALIZED_ATTRS: + if col in RESERVED_FIELD_NAMES: return self.simple_error('', _('The lookup name %s is reserved and cannot be used')%col) bad_col = False if col in self.parent.custcols: diff --git a/src/calibre/gui2/search_box.py b/src/calibre/gui2/search_box.py index 575f5563d6..8627802ef4 100644 --- a/src/calibre/gui2/search_box.py +++ b/src/calibre/gui2/search_box.py @@ -135,7 +135,6 @@ class SearchBox2(QComboBox): def text_edited_slot(self, text): if self.as_you_type: - text = unicode(text) self.timer = self.startTimer(self.__class__.INTERVAL) def timerEvent(self, event): diff --git a/src/calibre/manual/faq.rst b/src/calibre/manual/faq.rst index 6d73da5e50..f7329fb54d 100644 --- a/src/calibre/manual/faq.rst +++ b/src/calibre/manual/faq.rst @@ -145,7 +145,7 @@ First perform the following steps in |app| For an iPad: -Install the ReadMe app on your iPad using iTunes. Open Safari and browse to:: +Install the ReadMe app on your iPad using iTunes. Open the Readme builtin browser and browse to:: http://192.168.1.2:8080/ diff --git a/src/calibre/translations/calibre.pot b/src/calibre/translations/calibre.pot index b28875eb8f..d3d307108e 100644 --- a/src/calibre/translations/calibre.pot +++ b/src/calibre/translations/calibre.pot @@ -4,9 +4,9 @@ # msgid "" msgstr "" -"Project-Id-Version: calibre 0.6.53\n" -"POT-Creation-Date: 2010-05-15 21:26+MDT\n" -"PO-Revision-Date: 2010-05-15 21:26+MDT\n" +"Project-Id-Version: calibre 0.6.54\n" +"POT-Creation-Date: 2010-05-21 15:44+MDT\n" +"PO-Revision-Date: 2010-05-21 15:44+MDT\n" "Last-Translator: Automatically generated\n" "Language-Team: LANGUAGE\n" "MIME-Version: 1.0\n" @@ -95,8 +95,8 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/ebooks/pdf/manipulate/rotate.py:63 #: /home/kovid/work/calibre/src/calibre/ebooks/pdf/manipulate/split.py:81 #: /home/kovid/work/calibre/src/calibre/ebooks/pdf/manipulate/split.py:82 -#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/writer.py:28 -#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/writer.py:29 +#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/writer.py:87 +#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/writer.py:88 #: /home/kovid/work/calibre/src/calibre/ebooks/rtf/input.py:233 #: /home/kovid/work/calibre/src/calibre/ebooks/rtf/input.py:235 #: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:279 @@ -135,7 +135,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/library/server.py:671 #: /home/kovid/work/calibre/src/calibre/library/server.py:747 #: /home/kovid/work/calibre/src/calibre/library/server.py:794 -#: /home/kovid/work/calibre/src/calibre/utils/localization.py:113 +#: /home/kovid/work/calibre/src/calibre/utils/localization.py:114 #: /home/kovid/work/calibre/src/calibre/utils/podofo/__init__.py:45 #: /home/kovid/work/calibre/src/calibre/utils/podofo/__init__.py:63 #: /home/kovid/work/calibre/src/calibre/utils/podofo/__init__.py:77 @@ -247,7 +247,7 @@ msgid "This profile tries to provide sane defaults and is useful if you know not msgstr "" #: /home/kovid/work/calibre/src/calibre/customize/profiles.py:57 -#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:256 +#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:258 msgid "This profile is intended for the SONY PRS line. The 500/505/600/700 etc." msgstr "" @@ -256,62 +256,62 @@ msgid "This profile is intended for the SONY PRS 300." msgstr "" #: /home/kovid/work/calibre/src/calibre/customize/profiles.py:78 -#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:290 +#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:292 msgid "This profile is intended for the SONY PRS-900." msgstr "" #: /home/kovid/work/calibre/src/calibre/customize/profiles.py:86 -#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:320 +#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:322 msgid "This profile is intended for the Microsoft Reader." msgstr "" #: /home/kovid/work/calibre/src/calibre/customize/profiles.py:97 -#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:331 +#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:333 msgid "This profile is intended for the Mobipocket books." msgstr "" #: /home/kovid/work/calibre/src/calibre/customize/profiles.py:110 -#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:344 +#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:346 msgid "This profile is intended for the Hanlin V3 and its clones." msgstr "" #: /home/kovid/work/calibre/src/calibre/customize/profiles.py:122 -#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:356 +#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:358 msgid "This profile is intended for the Hanlin V5 and its clones." msgstr "" #: /home/kovid/work/calibre/src/calibre/customize/profiles.py:132 -#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:364 +#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:366 msgid "This profile is intended for the Cybook G3." msgstr "" #: /home/kovid/work/calibre/src/calibre/customize/profiles.py:145 -#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:377 +#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:379 msgid "This profile is intended for the Cybook Opus." msgstr "" #: /home/kovid/work/calibre/src/calibre/customize/profiles.py:157 -#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:388 +#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:390 msgid "This profile is intended for the Amazon Kindle." msgstr "" #: /home/kovid/work/calibre/src/calibre/customize/profiles.py:169 -#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:423 +#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:425 msgid "This profile is intended for the Irex Illiad." msgstr "" #: /home/kovid/work/calibre/src/calibre/customize/profiles.py:181 -#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:436 +#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:438 msgid "This profile is intended for the IRex Digital Reader 1000." msgstr "" #: /home/kovid/work/calibre/src/calibre/customize/profiles.py:194 -#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:450 +#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:452 msgid "This profile is intended for the IRex Digital Reader 800." msgstr "" #: /home/kovid/work/calibre/src/calibre/customize/profiles.py:206 -#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:464 +#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:466 msgid "This profile is intended for the B&N Nook." msgstr "" @@ -323,23 +323,27 @@ msgstr "" msgid "This profile tries to provide sane defaults and is useful if you want to produce a document intended to be read at a computer or on a range of devices." msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:269 +#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:248 +msgid "Intended for the iPad and similar devices with a resolution of 768x1024" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:271 msgid "This profile is intended for the Kobo Reader." msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:281 +#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:283 msgid "This profile is intended for the SONY PRS-300." msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:299 +#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:301 msgid "This profile is intended for the 5-inch JetBook." msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:308 +#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:310 msgid "This profile is intended for the SONY PRS line. The 500/505/700 etc, in landscape mode. Mainly useful for comics." msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:406 +#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:408 msgid "This profile is intended for the Amazon Kindle DX." msgstr "" @@ -403,11 +407,11 @@ msgstr "" msgid "Communicate with Android phones." msgstr "" -#: /home/kovid/work/calibre/src/calibre/devices/android/driver.py:36 +#: /home/kovid/work/calibre/src/calibre/devices/android/driver.py:39 msgid "Comma separated list of directories to send e-books to on the device. The first one that exists will be used" msgstr "" -#: /home/kovid/work/calibre/src/calibre/devices/android/driver.py:64 +#: /home/kovid/work/calibre/src/calibre/devices/android/driver.py:67 msgid "Communicate with S60 phones." msgstr "" @@ -523,7 +527,7 @@ msgstr "" msgid "Communicate with the Kobo Reader" msgstr "" -#: /home/kovid/work/calibre/src/calibre/devices/misc.py:55 +#: /home/kovid/work/calibre/src/calibre/devices/misc.py:56 msgid "Communicate with the Booq Avant" msgstr "" @@ -604,6 +608,14 @@ msgstr "" msgid "Communicate with the Teclast K3 reader." msgstr "" +#: /home/kovid/work/calibre/src/calibre/devices/teclast/driver.py:45 +msgid "Communicate with the Newsmy reader." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/devices/teclast/driver.py:60 +msgid "Communicate with the iPapyrus reader." +msgstr "" + #: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:252 msgid "Unable to detect the %s disk drive. Try rebooting." msgstr "" @@ -916,280 +928,288 @@ msgstr "" msgid "Output saved to" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:94 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:95 msgid "Level of verbosity. Specify multiple times for greater verbosity." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:101 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:102 msgid "Save the output from different stages of the conversion pipeline to the specified directory. Useful if you are unsure at which stage of the conversion process a bug is occurring." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:110 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:111 msgid "Specify the input profile. The input profile gives the conversion system information on how to interpret various information in the input document. For example resolution dependent lengths (i.e. lengths in pixels). Choices are:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:121 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:122 msgid "Specify the output profile. The output profile tells the conversion system how to optimize the created document for the specified device. In some cases, an output profile is required to produce documents that will work on a device. For example EPUB on the SONY reader. Choices are:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:132 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:133 msgid "The base font size in pts. All font sizes in the produced book will be rescaled based on this size. By choosing a larger size you can make the fonts in the output bigger and vice versa. By default, the base font size is chosen based on the output profile you chose." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:142 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:143 msgid "Mapping from CSS font names to font sizes in pts. An example setting is 12,12,14,16,18,20,22,24. These are the mappings for the sizes xx-small to xx-large, with the final size being for huge fonts. The font rescaling algorithm uses these sizes to intelligently rescale fonts. The default is to use a mapping based on the output profile you chose." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:154 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:155 msgid "Disable all rescaling of font sizes." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:161 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:162 msgid "The line height in pts. Controls spacing between consecutive lines of text. By default no line height manipulation is performed." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:169 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:170 msgid "Some badly designed documents use tables to control the layout of text on the page. When converted these documents often have text that runs off the page and other artifacts. This option will extract the content from the tables and present it in a linear fashion." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:179 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:180 msgid "XPath expression that specifies all tags that should be added to the Table of Contents at level one. If this is specified, it takes precedence over other forms of auto-detection." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:188 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:189 msgid "XPath expression that specifies all tags that should be added to the Table of Contents at level two. Each entry is added under the previous level one entry." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:196 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:197 msgid "XPath expression that specifies all tags that should be added to the Table of Contents at level three. Each entry is added under the previous level two entry." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:204 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:205 msgid "Normally, if the source file already has a Table of Contents, it is used in preference to the auto-generated one. With this option, the auto-generated one is always used." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:212 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:213 msgid "Don't add auto-detected chapters to the Table of Contents." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:219 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:220 msgid "If fewer than this number of chapters is detected, then links are added to the Table of Contents. Default: %default" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:226 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:227 msgid "Maximum number of links to insert into the TOC. Set to 0 to disable. Default is: %default. Links are only added to the TOC if less than the threshold number of chapters were detected." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:234 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:235 msgid "Remove entries from the Table of Contents whose titles match the specified regular expression. Matching entries and all their children are removed." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:245 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:246 msgid "An XPath expression to detect chapter titles. The default is to consider

or

tags that contain the words \"chapter\",\"book\",\"section\" or \"part\" as chapter titles as well as any tags that have class=\"chapter\". The expression used must evaluate to a list of elements. To disable chapter detection, use the expression \"/\". See the XPath Tutorial in the calibre User Manual for further help on using this feature." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:259 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:260 msgid "Specify how to mark detected chapters. A value of \"pagebreak\" will insert page breaks before chapters. A value of \"rule\" will insert a line before chapters. A value of \"none\" will disable chapter marking and a value of \"both\" will use both page breaks and lines to mark chapters." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:269 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:270 msgid "Either the path to a CSS stylesheet or raw CSS. This CSS will be appended to the style rules from the source file, so it can be used to override those rules." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:278 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:279 msgid "An XPath expression. Page breaks are inserted before the specified elements." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:284 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:285 msgid "Set the top margin in pts. Default is %default. Note: 72 pts equals 1 inch" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:289 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:290 msgid "Set the bottom margin in pts. Default is %default. Note: 72 pts equals 1 inch" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:294 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:295 msgid "Set the left margin in pts. Default is %default. Note: 72 pts equals 1 inch" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:299 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:300 msgid "Set the right margin in pts. Default is %default. Note: 72 pts equals 1 inch" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:305 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:306 msgid "Change text justification. A value of \"left\" converts all justified text in the source to left aligned (i.e. unjustified) text. A value of \"justify\" converts all unjustified text to justified. A value of \"original\" (the default) does not change justification in the source file. Note that only some output formats support justification." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:315 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:316 msgid "Remove spacing between paragraphs. Also sets an indent on paragraphs of 1.5em. Spacing removal will not work if the source file does not use paragraphs (

or

tags)." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:322 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:323 msgid "When calibre removes inter paragraph spacing, it automatically sets a paragraph indent, to ensure that paragraphs can be easily distinguished. This option controls the width of that indent." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:329 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:330 msgid "Use the cover detected from the source file in preference to the specified cover." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:335 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:336 msgid "Insert a blank line between paragraphs. Will not work if the source file does not use paragraphs (

or

tags)." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:342 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:343 msgid "Remove the first image from the input ebook. Useful if the first image in the source file is a cover and you are specifying an external cover." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:350 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:351 msgid "Insert the book metadata at the start of the book. This is useful if your ebook reader does not support displaying/searching metadata directly." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:358 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:359 msgid "Attempt to detect and correct hard line breaks and other problems in the source file. This may make things worse, so use with care." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:366 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:367 msgid "Use a regular expression to try and remove the header." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:373 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:374 msgid "The regular expression to use to remove the header." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:379 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:380 msgid "Use a regular expression to try and remove the footer." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:386 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:387 msgid "The regular expression to use to remove the footer." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:393 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:394 msgid "Read metadata from the specified OPF file. Metadata read from this file will override any metadata in the source file." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:400 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:401 msgid "Transliterate unicode characters to an ASCII representation. Use with care because this will replace unicode characters with ASCII. For instance it will replace \"%s\" with \"Mikhail Gorbachiov\". Also, note that in cases where there are multiple representations of a character (characters shared by Chinese and Japanese for instance) the representation used by the largest number of people will be used (Chinese in the previous example)." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:415 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:416 +msgid "Preserve ligatures present in the input document. A ligature is a special rendering of a pair of characters like ff, fi, fl et cetera. Most readers do not have support for ligatures in their default fonts, so they are unlikely to render correctly. By default, calibre will turn a ligature into the corresponding pair of normal characters. This option will preserve them instead." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:428 #: /home/kovid/work/calibre/src/calibre/ebooks/metadata/cli.py:38 msgid "Set the title." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:419 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:432 msgid "Set the authors. Multiple authors should be separated by ampersands." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:424 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:437 msgid "The version of the title to be used for sorting. " msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:428 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:441 msgid "String to be used when sorting by author. " msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:432 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:445 msgid "Set the cover to the specified file or URL" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:436 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:449 #: /home/kovid/work/calibre/src/calibre/ebooks/metadata/cli.py:54 msgid "Set the ebook description." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:440 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:453 #: /home/kovid/work/calibre/src/calibre/ebooks/metadata/cli.py:56 msgid "Set the ebook publisher." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:444 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:457 #: /home/kovid/work/calibre/src/calibre/ebooks/metadata/cli.py:60 msgid "Set the series this ebook belongs to." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:448 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:461 #: /home/kovid/work/calibre/src/calibre/ebooks/metadata/cli.py:62 msgid "Set the index of the book in this series." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:452 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:465 #: /home/kovid/work/calibre/src/calibre/ebooks/metadata/cli.py:64 msgid "Set the rating. Should be a number between 1 and 5." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:456 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:469 #: /home/kovid/work/calibre/src/calibre/ebooks/metadata/cli.py:66 msgid "Set the ISBN of the book." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:460 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:473 #: /home/kovid/work/calibre/src/calibre/ebooks/metadata/cli.py:68 msgid "Set the tags for the book. Should be a comma separated list." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:464 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:477 #: /home/kovid/work/calibre/src/calibre/ebooks/metadata/cli.py:70 msgid "Set the book producer." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:468 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:481 #: /home/kovid/work/calibre/src/calibre/ebooks/metadata/cli.py:72 msgid "Set the language." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:472 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:485 msgid "Set the publication date." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:476 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:489 msgid "Set the book timestamp (used by the date column in calibre)." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:576 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:589 msgid "Could not find an ebook inside the archive" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:634 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:647 msgid "Values of series index and rating must be numbers. Ignoring" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:641 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:654 msgid "Failed to parse date/time" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:788 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:802 msgid "Converting input to HTML..." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:815 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:829 msgid "Running transforms on ebook..." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:902 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:916 msgid "Creating" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/epub/output.py:58 +#: /home/kovid/work/calibre/src/calibre/ebooks/epub/output.py:205 msgid "Extract the contents of the generated EPUB file to the specified directory. The contents of the directory are first deleted, so be careful." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/epub/output.py:64 +#: /home/kovid/work/calibre/src/calibre/ebooks/epub/output.py:211 msgid "Turn off splitting at page breaks. Normally, input files are automatically split at every page break into two files. This gives an output ebook that can be parsed faster and with less resources. However, splitting is slow and if your source file contains a very large number of page breaks, you should turn off splitting on page breaks." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/epub/output.py:75 +#: /home/kovid/work/calibre/src/calibre/ebooks/epub/output.py:222 msgid "Split all HTML files larger than this size (in KB). This is necessary as most EPUB readers cannot handle large file sizes. The default of %defaultKB is the size required for Adobe Digital Editions." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/epub/output.py:82 +#: /home/kovid/work/calibre/src/calibre/ebooks/epub/output.py:229 msgid "Normally, if the input file has no cover and you don't specify one, a default cover is generated with the title, authors, etc. This option disables the generation of this cover." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/epub/output.py:88 +#: /home/kovid/work/calibre/src/calibre/ebooks/epub/output.py:235 msgid "Do not use SVG for the book cover. Use this option if your EPUB is going to be used ona device that does not support SVG, like the iPhone or the JetBook Lite. Without this option, such devices will display the cover as a blank page." msgstr "" +#: /home/kovid/work/calibre/src/calibre/ebooks/epub/output.py:243 +msgid "When using an SVG cover, this option will cause the cover to scale to cover the available screen area, but still preserve its aspect ratio (ratio of width to height). That means there may be white borders at the sides or top and bottom of the image, but the image will never be distorted. Without this option the image may be slightly distorted, but there will be no borders." +msgstr "" + #: /home/kovid/work/calibre/src/calibre/ebooks/fb2/fb2ml.py:144 #: /home/kovid/work/calibre/src/calibre/ebooks/rb/rbml.py:102 #: /home/kovid/work/calibre/src/calibre/ebooks/txt/txtml.py:77 @@ -2026,19 +2046,19 @@ msgstr "" msgid "Split Options:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/output.py:31 +#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/output.py:59 msgid "The unit of measure. Default is inch. Choices are %s Note: This does not override the unit for margins!" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/output.py:36 +#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/output.py:64 msgid "The size of the paper. This size will be overridden when an output profile is used. Default is letter. Choices are %s" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/output.py:40 +#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/output.py:68 msgid "Custom size of the document. Use the form widthxheight EG. `123x321` to specify the width and height. This overrides any specified paper-size." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/output.py:45 +#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/output.py:73 msgid "The orientation of the page. Default is portrait. Choices are %s" msgstr "" @@ -2307,10 +2327,10 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/catalog/catalog_tab_template_ui.py:27 #: /home/kovid/work/calibre/src/calibre/gui2/convert/comic_input_ui.py:88 #: /home/kovid/work/calibre/src/calibre/gui2/convert/debug_ui.py:49 -#: /home/kovid/work/calibre/src/calibre/gui2/convert/epub_output_ui.py:44 +#: /home/kovid/work/calibre/src/calibre/gui2/convert/epub_output_ui.py:48 #: /home/kovid/work/calibre/src/calibre/gui2/convert/fb2_input_ui.py:28 #: /home/kovid/work/calibre/src/calibre/gui2/convert/fb2_output_ui.py:28 -#: /home/kovid/work/calibre/src/calibre/gui2/convert/look_and_feel_ui.py:125 +#: /home/kovid/work/calibre/src/calibre/gui2/convert/look_and_feel_ui.py:119 #: /home/kovid/work/calibre/src/calibre/gui2/convert/lrf_output_ui.py:115 #: /home/kovid/work/calibre/src/calibre/gui2/convert/metadata_ui.py:166 #: /home/kovid/work/calibre/src/calibre/gui2/convert/mobi_output_ui.py:66 @@ -2512,7 +2532,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/convert/debug_ui.py:52 #: /home/kovid/work/calibre/src/calibre/gui2/convert/debug_ui.py:53 -#: /home/kovid/work/calibre/src/calibre/gui2/convert/look_and_feel_ui.py:131 +#: /home/kovid/work/calibre/src/calibre/gui2/convert/look_and_feel_ui.py:125 #: /home/kovid/work/calibre/src/calibre/gui2/convert/metadata_ui.py:171 #: /home/kovid/work/calibre/src/calibre/gui2/convert/xexp_edit_ui.py:52 #: /home/kovid/work/calibre/src/calibre/gui2/device_drivers/configwidget_ui.py:76 @@ -2558,26 +2578,30 @@ msgstr "" msgid "EPUB Output" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/convert/epub_output_ui.py:45 +#: /home/kovid/work/calibre/src/calibre/gui2/convert/epub_output_ui.py:49 msgid "Do not &split on page breaks" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/convert/epub_output_ui.py:46 -msgid "Split files &larger than:" -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/convert/epub_output_ui.py:47 -msgid " KB" -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/convert/epub_output_ui.py:48 +#: /home/kovid/work/calibre/src/calibre/gui2/convert/epub_output_ui.py:50 msgid "No default &cover" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/convert/epub_output_ui.py:49 +#: /home/kovid/work/calibre/src/calibre/gui2/convert/epub_output_ui.py:51 msgid "No &SVG cover" msgstr "" +#: /home/kovid/work/calibre/src/calibre/gui2/convert/epub_output_ui.py:52 +msgid "Preserve cover &aspect ratio" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/convert/epub_output_ui.py:53 +msgid "Split files &larger than:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/convert/epub_output_ui.py:54 +msgid " KB" +msgstr "" + #: /home/kovid/work/calibre/src/calibre/gui2/convert/fb2_input.py:12 msgid "FB2 Input" msgstr "" @@ -2618,15 +2642,15 @@ msgid "&Base font size:" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/convert/font_key_ui.py:105 -#: /home/kovid/work/calibre/src/calibre/gui2/convert/look_and_feel_ui.py:129 +#: /home/kovid/work/calibre/src/calibre/gui2/convert/look_and_feel_ui.py:123 msgid "Font size &key:" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/convert/font_key_ui.py:106 #: /home/kovid/work/calibre/src/calibre/gui2/convert/font_key_ui.py:110 #: /home/kovid/work/calibre/src/calibre/gui2/convert/font_key_ui.py:112 -#: /home/kovid/work/calibre/src/calibre/gui2/convert/look_and_feel_ui.py:128 -#: /home/kovid/work/calibre/src/calibre/gui2/convert/look_and_feel_ui.py:133 +#: /home/kovid/work/calibre/src/calibre/gui2/convert/look_and_feel_ui.py:122 +#: /home/kovid/work/calibre/src/calibre/gui2/convert/look_and_feel_ui.py:127 #: /home/kovid/work/calibre/src/calibre/gui2/convert/lrf_output_ui.py:118 #: /home/kovid/work/calibre/src/calibre/gui2/convert/lrf_output_ui.py:120 #: /home/kovid/work/calibre/src/calibre/gui2/convert/lrf_output_ui.py:125 @@ -2665,72 +2689,76 @@ msgstr "" msgid "Control the look and feel of the output" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/convert/look_and_feel_ui.py:126 +#: /home/kovid/work/calibre/src/calibre/gui2/convert/look_and_feel.py:30 +msgid "Original" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/convert/look_and_feel.py:31 +msgid "Left align" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/convert/look_and_feel.py:32 +msgid "Justify text" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/convert/look_and_feel_ui.py:120 msgid "&Disable font size rescaling" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/convert/look_and_feel_ui.py:127 +#: /home/kovid/work/calibre/src/calibre/gui2/convert/look_and_feel_ui.py:121 msgid "Base &font size:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/convert/look_and_feel_ui.py:130 +#: /home/kovid/work/calibre/src/calibre/gui2/convert/look_and_feel_ui.py:124 msgid "Wizard to help you choose an appropriate font size key" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/convert/look_and_feel_ui.py:132 +#: /home/kovid/work/calibre/src/calibre/gui2/convert/look_and_feel_ui.py:126 msgid "Line &height:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/convert/look_and_feel_ui.py:134 +#: /home/kovid/work/calibre/src/calibre/gui2/convert/look_and_feel_ui.py:128 msgid "Input character &encoding:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/convert/look_and_feel_ui.py:135 +#: /home/kovid/work/calibre/src/calibre/gui2/convert/look_and_feel_ui.py:129 msgid "Remove &spacing between paragraphs" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/convert/look_and_feel_ui.py:136 +#: /home/kovid/work/calibre/src/calibre/gui2/convert/look_and_feel_ui.py:130 msgid "Indent size:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/convert/look_and_feel_ui.py:137 +#: /home/kovid/work/calibre/src/calibre/gui2/convert/look_and_feel_ui.py:131 msgid "

When calibre removes inter paragraph spacing, it automatically sets a paragraph indent, to ensure that paragraphs can be easily distinguished. This option controls the width of that indent." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/convert/look_and_feel_ui.py:138 +#: /home/kovid/work/calibre/src/calibre/gui2/convert/look_and_feel_ui.py:132 msgid " em" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/convert/look_and_feel_ui.py:139 -msgid "&Linearize tables" -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/convert/look_and_feel_ui.py:140 -msgid "&Transliterate unicode characters to ASCII." -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/convert/look_and_feel_ui.py:141 -msgid "Extra &CSS" -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/convert/look_and_feel_ui.py:142 -msgid "Insert &blank line" -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/convert/look_and_feel_ui.py:143 +#: /home/kovid/work/calibre/src/calibre/gui2/convert/look_and_feel_ui.py:133 msgid "Text justification:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/convert/look_and_feel_ui.py:144 -msgid "justify" +#: /home/kovid/work/calibre/src/calibre/gui2/convert/look_and_feel_ui.py:134 +msgid "&Linearize tables" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/convert/look_and_feel_ui.py:145 -msgid "left" +#: /home/kovid/work/calibre/src/calibre/gui2/convert/look_and_feel_ui.py:135 +msgid "Extra &CSS" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/convert/look_and_feel_ui.py:146 -msgid "original" +#: /home/kovid/work/calibre/src/calibre/gui2/convert/look_and_feel_ui.py:136 +msgid "&Transliterate unicode characters to ASCII" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/convert/look_and_feel_ui.py:137 +msgid "Insert &blank line" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/convert/look_and_feel_ui.py:138 +msgid "Keep &ligatures" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/convert/lrf_output.py:19 @@ -7833,18 +7861,22 @@ msgid "English (Ireland)" msgstr "" #: /home/kovid/work/calibre/src/calibre/utils/localization.py:109 -msgid "Spanish (Paraguay)" +msgid "English (China)" msgstr "" #: /home/kovid/work/calibre/src/calibre/utils/localization.py:110 -msgid "German (AT)" +msgid "Spanish (Paraguay)" msgstr "" #: /home/kovid/work/calibre/src/calibre/utils/localization.py:111 -msgid "Dutch (NL)" +msgid "German (AT)" msgstr "" #: /home/kovid/work/calibre/src/calibre/utils/localization.py:112 +msgid "Dutch (NL)" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/utils/localization.py:113 msgid "Dutch (BE)" msgstr "" @@ -7868,13 +7900,13 @@ msgstr "" msgid "Control email delivery" msgstr "" -#: /home/kovid/work/calibre/src/calibre/web/feeds/__init__.py:102 -#: /home/kovid/work/calibre/src/calibre/web/feeds/__init__.py:124 +#: /home/kovid/work/calibre/src/calibre/web/feeds/__init__.py:113 +#: /home/kovid/work/calibre/src/calibre/web/feeds/__init__.py:135 msgid "Unknown feed" msgstr "" -#: /home/kovid/work/calibre/src/calibre/web/feeds/__init__.py:142 -#: /home/kovid/work/calibre/src/calibre/web/feeds/__init__.py:165 +#: /home/kovid/work/calibre/src/calibre/web/feeds/__init__.py:153 +#: /home/kovid/work/calibre/src/calibre/web/feeds/__init__.py:178 msgid "Untitled article" msgstr "" @@ -7970,23 +8002,23 @@ msgstr "" msgid "Untitled Article" msgstr "" -#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1228 +#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1230 msgid "Article downloaded: %s" msgstr "" -#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1239 +#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1241 msgid "Article download failed: %s" msgstr "" -#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1256 +#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1258 msgid "Fetching feed" msgstr "" -#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1403 +#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1405 msgid "Failed to log in, check your username and password for the calibre Periodicals service." msgstr "" -#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1419 +#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1421 msgid "You do not have permission to download this issue. Either your subscription has expired or you have exceeded the maximum allowed downloads for today." msgstr "" diff --git a/src/calibre/web/feeds/__init__.py b/src/calibre/web/feeds/__init__.py index c633e3b62b..bcb8c2d74f 100644 --- a/src/calibre/web/feeds/__init__.py +++ b/src/calibre/web/feeds/__init__.py @@ -49,6 +49,17 @@ class Article(object): self.date = published self.utctime = dt_factory(self.date, assume_utc=True, as_utc=True) self.localtime = self.utctime.astimezone(local_tz) + self._formatted_date = None + + @dynamic_property + def formatted_date(self): + def fget(self): + if self._formatted_date is None: + self._formatted_date = self.localtime.strftime(" [%a, %d %b %H:%M]") + return self._formatted_date + def fset(self, val): + self._formatted_date = val + return property(fget=fget, fset=fset) @dynamic_property def title(self): @@ -150,6 +161,8 @@ class Feed(object): self.articles.append(article) else: self.logger.debug('Skipping article %s (%s) from feed %s as it is too old.'%(title, article.localtime.strftime('%a, %d %b, %Y %H:%M'), self.title)) + d = item.get('date', '') + article.formatted_date = d def parse_article(self, item): diff --git a/src/calibre/web/feeds/news.py b/src/calibre/web/feeds/news.py index 46e8cd005d..db4ce3fda3 100644 --- a/src/calibre/web/feeds/news.py +++ b/src/calibre/web/feeds/news.py @@ -1179,6 +1179,8 @@ class BasicNewsRecipe(Recipe): body.insert(len(body.contents), elem) with open(last, 'wb') as fi: fi.write(unicode(soup).encode('utf-8')) + if len(feeds) == 0: + raise Exception('All feeds are empty, aborting.') if len(feeds) > 1: for i, f in enumerate(feeds): diff --git a/src/calibre/web/feeds/templates.py b/src/calibre/web/feeds/templates.py index 954677a90e..4b2156b6a1 100644 --- a/src/calibre/web/feeds/templates.py +++ b/src/calibre/web/feeds/templates.py @@ -160,7 +160,7 @@ class FeedTemplate(Template):

  • ${article.title} - +
    ${Markup(cutoff(article.text_summary))}