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
+
+
+
+
+
+
+
+
+ '''
+
+ 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
-
-
-
-
-
-
-
-
- '''
-
- 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/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
+
+
+
+
+
+
+
+
+ '''%(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/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/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):