diff --git a/imgsrc/rating.svg b/imgsrc/rating.svg
new file mode 100644
index 0000000000..d289c71b99
--- /dev/null
+++ b/imgsrc/rating.svg
@@ -0,0 +1,589 @@
+
+
+
diff --git a/resources/images/rating.png b/resources/images/rating.png
new file mode 100644
index 0000000000..81eba505b9
Binary files /dev/null and b/resources/images/rating.png differ
diff --git a/resources/images/star.png b/resources/images/star.png
deleted file mode 100644
index 6eb1fb890f..0000000000
Binary files a/resources/images/star.png and /dev/null differ
diff --git a/resources/jacket/stylesheet.css b/resources/jacket/stylesheet.css
new file mode 100644
index 0000000000..8dee8edc3c
--- /dev/null
+++ b/resources/jacket/stylesheet.css
@@ -0,0 +1,116 @@
+/*
+** Book Jacket generation
+**
+** The template for Book Jackets is template.xhtml
+** This CSS is inserted into the generated HTML at conversion time
+**
+** Users can control parts of the presentation of a generated book jacket by
+** editing this file and template.xhtml
+**
+** The general form of a generated Book Jacket:
+**
+** Title
+** Series: series [series_index]
+** Published: year_of_publication
+** Rating: #_of_stars
+** Tags: tag1, tag2, tag3 ...
+**
+** Comments
+**
+** If a book does not have Series information, a date of publication, a rating or tags
+** the corresponding row is automatically removed from the generated book jacket.
+*/
+
+/*
+** Banner
+** Only affects EPUB, kindle ignores this type of formatting
+*/
+.cbj_banner {
+ background: #eee;
+ border: thin solid black;
+ margin: 1em;
+ padding: 1em;
+ -webkit-border-radius:8px;
+ }
+
+/*
+** Title
+*/
+.cbj_title {
+ font-size: x-large;
+ text-align: center;
+ }
+
+/*
+** Table containing Series, Publication Year, Rating and Tags
+*/
+table.cbj_header {
+ width: 100%;
+ }
+
+/*
+** General formatting for banner labels
+*/
+table.cbj_header td.cbj_label {
+ font-family: sans-serif;
+ font-weight: bold;
+ text-align: right;
+ width: 40%;
+ }
+
+/*
+** General formatting for banner content
+*/
+table.cbj_header td.cbj_content {
+ font-family: sans-serif;
+ text-align: left;
+ width:60%;
+ }
+
+/*
+** To skip a banner item (Series|Published|Rating|Tags),
+** edit the appropriate CSS rule below.
+*/
+table.cbj_header tr.cbj_series {
+ /* Uncomment the next line to remove 'Series' from banner section */
+ /* display:none; */
+ }
+
+table.cbj_header tr.cbj_pubdate {
+ /* Uncomment the next line to remove 'Published' from banner section */
+ /* display:none; */
+ }
+
+table.cbj_header tr.cbj_rating {
+ /* Uncomment the next line to remove 'Rating' from banner section */
+ /* display:none; */
+ }
+
+table.cbj_header tr.cbj_tags {
+ /* Uncomment the next line to remove 'Tags' from banner section */
+ /* display:none; */
+ }
+
+hr {
+ /* This rule controls formatting for any hr elements contained in the jacket */
+ border-top: 0px solid white;
+ border-right: 0px solid white;
+ border-bottom: 2px solid black;
+ border-left: 0px solid white;
+ margin-left: 10%;
+ width: 80%;
+ }
+
+.cbj_footer {
+ font-family: sans-serif;
+ font-size: small;
+ margin-top: 8px;
+ text-align: center;
+ }
+.cbj_smallcaps {
+ font-size: 90%;
+ }
+
+.cbj_comments {
+ font-family: sans-serif;
+ }
diff --git a/resources/jacket/template.xhtml b/resources/jacket/template.xhtml
new file mode 100644
index 0000000000..93e12983e8
--- /dev/null
+++ b/resources/jacket/template.xhtml
@@ -0,0 +1,34 @@
+
+
+ {title_str}
+
+
+
+
+
+
+
+
+
+
diff --git a/resources/recipes/slate.recipe b/resources/recipes/slate.recipe
index c03255d2df..9da1c4da78 100644
--- a/resources/recipes/slate.recipe
+++ b/resources/recipes/slate.recipe
@@ -27,9 +27,6 @@ class PeriodicalNameHere(BasicNewsRecipe):
encoding = None
language = 'en'
-
-
-
# Method variables for customizing feed parsing
summary_length = 250
use_embedded_content = None
@@ -45,13 +42,26 @@ class PeriodicalNameHere(BasicNewsRecipe):
match_regexps = []
# The second entry is for 'Big Money', which comes from a different site, uses different markup
- keep_only_tags = [dict(attrs={ 'id':['article_top', 'article_body']}),
+ keep_only_tags = [dict(attrs={ 'id':['article_top', 'article_body', 'story']}),
dict(attrs={ 'id':['content']}) ]
# The second entry is for 'Big Money', which comes from a different site, uses different markup
- remove_tags = [dict(attrs={ 'id':['toolbox','recommend_tab','insider_ad_wrapper',
- 'article_bottom_tools_cntr','fray_article_discussion', 'fray_article_links','bottom_sponsored_links','author_bio',
- 'bizbox_links_bottom','ris_links_wrapper','BOXXLE']}),
+ remove_tags = [dict(attrs={ 'id':[
+ 'add_comments_button',
+ 'article_bottom_tools',
+ 'article_bottom_tools_cntr',
+ 'bizbox_links_bottom',
+ 'BOXXLE',
+ 'comments_button',
+ 'comments-to-fray',
+ 'fbog_article_bottom_cntr',
+ 'fray_article_discussion', 'fray_article_links','bottom_sponsored_links','author_bio',
+ 'insider_ad_wrapper',
+ 'js_kit_cntr',
+ 'recommend_tab',
+ 'ris_links_wrapper',
+ 'toolbox',
+ ]}),
dict(attrs={ 'id':['content-top','service-links-bottom','hed']}) ]
excludedDescriptionKeywords = ['Slate V','Twitter feed','podcast']
@@ -339,8 +349,8 @@ class PeriodicalNameHere(BasicNewsRecipe):
# Change to
headline = soup.find("h1")
- tag = headline.find("span")
- tag.name = 'div'
+ #tag = headline.find("span")
+ #tag.name = 'div'
if headline is not None :
h2tag = Tag(soup, "h2")
diff --git a/src/calibre/devices/apple/driver.py b/src/calibre/devices/apple/driver.py
index 52383e847f..da286bcc6e 100644
--- a/src/calibre/devices/apple/driver.py
+++ b/src/calibre/devices/apple/driver.py
@@ -2343,8 +2343,10 @@ class ITUNES(DriverBase):
if isosx:
if DEBUG:
self.log.info(" deleting '%s' from iDevice" % cached_book['title'])
- cached_book['dev_book'].delete()
-
+ try:
+ cached_book['dev_book'].delete()
+ except:
+ self.log.error(" error deleting '%s'" % cached_book['title'])
elif iswindows:
hit = self._find_device_book(cached_book)
if hit:
diff --git a/src/calibre/devices/prs505/driver.py b/src/calibre/devices/prs505/driver.py
index c55936be2d..4c14565c2d 100644
--- a/src/calibre/devices/prs505/driver.py
+++ b/src/calibre/devices/prs505/driver.py
@@ -36,15 +36,15 @@ class PRS505(USBMS):
VENDOR_NAME = 'SONY'
WINDOWS_MAIN_MEM = re.compile(
r'(PRS-(505|300|500))|'
- r'(PRS-((700[#/])|((6|9)00&)))'
+ r'(PRS-((700[#/])|((6|9|3)(0|5)0&)))'
)
WINDOWS_CARD_A_MEM = re.compile(
r'(PRS-(505|500)[#/]\S+:MS)|'
- r'(PRS-((700[/#]\S+:)|((6|9)00[#_]))MS)'
+ r'(PRS-((700[/#]\S+:)|((6|9)(0|5)0[#_]))MS)'
)
WINDOWS_CARD_B_MEM = re.compile(
r'(PRS-(505|500)[#/]\S+:SD)|'
- r'(PRS-((700[/#]\S+:)|((6|9)00[#_]))SD)'
+ r'(PRS-((700[/#]\S+:)|((6|9)(0|5)0[#_]))SD)'
)
diff --git a/src/calibre/ebooks/fb2/output.py b/src/calibre/ebooks/fb2/output.py
index d0125afe89..d6c7a25a90 100644
--- a/src/calibre/ebooks/fb2/output.py
+++ b/src/calibre/ebooks/fb2/output.py
@@ -28,6 +28,9 @@ class FB2Output(OutputFormatPlugin):
])
def convert(self, oeb_book, output_path, input_plugin, opts, log):
+ from calibre.ebooks.oeb.transforms.jacket import linearize_jacket
+ linearize_jacket(oeb_book)
+
fb2mlizer = FB2MLizer(log)
fb2_content = fb2mlizer.extract_content(oeb_book, opts)
diff --git a/src/calibre/ebooks/oeb/transforms/cover.py b/src/calibre/ebooks/oeb/transforms/cover.py
index 59b42df68a..532c9bbc03 100644
--- a/src/calibre/ebooks/oeb/transforms/cover.py
+++ b/src/calibre/ebooks/oeb/transforms/cover.py
@@ -99,7 +99,8 @@ class CoverManager(object):
series_string = None
if m.series and m.series_index:
series_string = _('Book %s of %s')%(
- fmt_sidx(m.series_index[0], use_roman=True), m.series[0])
+ fmt_sidx(m.series_index[0], use_roman=True),
+ unicode(m.series[0]))
try:
from calibre.ebooks import calibre_cover
diff --git a/src/calibre/ebooks/oeb/transforms/flatcss.py b/src/calibre/ebooks/oeb/transforms/flatcss.py
index ffdc641d1e..7212bd33c6 100644
--- a/src/calibre/ebooks/oeb/transforms/flatcss.py
+++ b/src/calibre/ebooks/oeb/transforms/flatcss.py
@@ -147,7 +147,6 @@ class CSSFlattener(object):
extra_css=css)
self.stylizers[item] = stylizer
-
def baseline_node(self, node, stylizer, sizes, csize):
csize = stylizer.style(node)['font-size']
if node.text:
@@ -195,7 +194,7 @@ class CSSFlattener(object):
value = 0.0
cssdict[property] = "%0.5fem" % (value / fsize)
- def flatten_node(self, node, stylizer, names, styles, psize, left=0):
+ def flatten_node(self, node, stylizer, names, styles, psize, item_id, left=0):
if not isinstance(node.tag, basestring) \
or namespace(node.tag) != XHTML_NS:
return
@@ -287,15 +286,18 @@ class CSSFlattener(object):
if self.lineh and 'line-height' not in cssdict:
lineh = self.lineh / psize
cssdict['line-height'] = "%0.5fem" % lineh
+
if (self.context.remove_paragraph_spacing or
self.context.insert_blank_line) and tag in ('p', 'div'):
- for prop in ('margin', 'padding', 'border'):
- for edge in ('top', 'bottom'):
- cssdict['%s-%s'%(prop, edge)] = '0pt'
+ if item_id != 'calibre_jacket' or self.context.output_profile.name == 'Kindle':
+ for prop in ('margin', 'padding', 'border'):
+ for edge in ('top', 'bottom'):
+ cssdict['%s-%s'%(prop, edge)] = '0pt'
if self.context.insert_blank_line:
cssdict['margin-top'] = cssdict['margin-bottom'] = '0.5em'
if self.context.remove_paragraph_spacing:
cssdict['text-indent'] = "%1.1fem" % self.context.remove_paragraph_spacing_indent_size
+
if cssdict:
items = cssdict.items()
items.sort()
@@ -314,7 +316,7 @@ class CSSFlattener(object):
if 'style' in node.attrib:
del node.attrib['style']
for child in node:
- self.flatten_node(child, stylizer, names, styles, psize, left)
+ self.flatten_node(child, stylizer, names, styles, psize, item_id, left)
def flatten_head(self, item, stylizer, href):
html = item.data
@@ -361,7 +363,7 @@ class CSSFlattener(object):
stylizer = self.stylizers[item]
body = html.find(XHTML('body'))
fsize = self.context.dest.fbase
- self.flatten_node(body, stylizer, names, styles, fsize)
+ self.flatten_node(body, stylizer, names, styles, fsize, item.id)
items = [(key, val) for (val, key) in styles.items()]
items.sort()
css = ''.join(".%s {\n%s;\n}\n\n" % (key, val) for key, val in items)
diff --git a/src/calibre/ebooks/oeb/transforms/jacket.py b/src/calibre/ebooks/oeb/transforms/jacket.py
index fec4d230c3..a44a18db95 100644
--- a/src/calibre/ebooks/oeb/transforms/jacket.py
+++ b/src/calibre/ebooks/oeb/transforms/jacket.py
@@ -6,139 +6,200 @@ __license__ = 'GPL v3'
__copyright__ = '2009, Kovid Goyal '
__docformat__ = 'restructuredtext en'
-import textwrap
+import sys
from xml.sax.saxutils import escape
-from itertools import repeat
from lxml import etree
-from calibre.ebooks.oeb.base import XPath, XPNSMAP
-from calibre import guess_type
+from calibre import guess_type, strftime
+from calibre.ebooks.BeautifulSoup import BeautifulSoup
+from calibre.ebooks.oeb.base import XPath, XHTML_NS, XHTML
from calibre.library.comments import comments_to_html
+
+JACKET_XPATH = '//h:meta[@name="calibre-content" and @content="jacket"]'
+
class Jacket(object):
'''
Book jacket manipulation. Remove first image and insert comments at start of
book.
'''
- JACKET_TEMPLATE = textwrap.dedent(u'''\
-
-
- %(title)s
-
-
-
-
-
-
%(title)s
-
%(jacket)s
-
%(series)s
-
%(rating)s
-
%(tags)s
-
-
- %(comments)s
-
-
-
-
- ''')
+ def remove_images(self, item, limit=1):
+ path = XPath('//h:img[@src]')
+ removed = 0
+ for img in path(item.data):
+ if removed >= limit:
+ break
+ href = item.abshref(img.get('src'))
+ image = self.oeb.manifest.hrefs.get(href, None)
+ if image is not None:
+ self.oeb.manifest.remove(image)
+ img.getparent().remove(img)
+ removed += 1
+ return removed
def remove_first_image(self):
- path = XPath('//h:img[@src]')
- for i, item in enumerate(self.oeb.spine):
- if i > 2: break
- for img in path(item.data):
- href = item.abshref(img.get('src'))
- image = self.oeb.manifest.hrefs.get(href, None)
- if image is not None:
- self.log('Removing first image', img.get('src'))
- self.oeb.manifest.remove(image)
- img.getparent().remove(img)
- return
-
- def get_rating(self, rating):
- ans = ''
- if rating is None:
- return
- try:
- num = float(rating)/2
- except:
- return ans
- num = max(0, num)
- num = min(num, 5)
- if num < 1:
- return ans
- id, href = self.oeb.manifest.generate('star', 'star.png')
- self.oeb.manifest.add(id, href, 'image/png', data=I('star.png', data=True))
- ans = 'Rating: ' + ''.join(repeat('
'%href, num))
- return ans
+ for item in self.oeb.spine:
+ removed = self.remove_images(item)
+ if removed > 0:
+ self.log('Removed first image')
+ break
def insert_metadata(self, mi):
self.log('Inserting metadata into book...')
- comments = mi.comments
- if not comments:
- try:
- comments = unicode(self.oeb.metadata.description[0])
- except:
- comments = ''
- if not comments.strip():
- comments = ''
- orig_comments = comments
- if comments:
- comments = comments_to_html(comments)
- series = 'Series: ' + escape(mi.series if mi.series else '')
- if mi.series and mi.series_index is not None:
- series += escape(' [%s]'%mi.format_series_index())
- if not mi.series:
- series = ''
- tags = mi.tags
- if not tags:
- try:
- tags = map(unicode, self.oeb.metadata.subject)
- except:
- tags = []
- if tags:
- tags = 'Tags: ' + self.opts.dest.tags_to_string(tags)
- else:
- tags = ''
+
try:
- title = mi.title if mi.title else unicode(self.oeb.metadata.title[0])
+ tags = map(unicode, self.oeb.metadata.subject)
+ except:
+ tags = []
+
+ try:
+ comments = unicode(self.oeb.metadata.description[0])
+ except:
+ comments = ''
+
+ try:
+ title = unicode(self.oeb.metadata.title[0])
except:
title = _('Unknown')
- def generate_html(comments):
- return self.JACKET_TEMPLATE%dict(xmlns=XPNSMAP['h'],
- title=escape(title), comments=comments,
- jacket=escape(_('Book Jacket')), series=series,
- tags=tags, rating=self.get_rating(mi.rating))
- id, href = self.oeb.manifest.generate('jacket', 'jacket.xhtml')
- from calibre.ebooks.oeb.base import RECOVER_PARSER, XPath
- try:
- root = etree.fromstring(generate_html(comments), parser=RECOVER_PARSER)
- except:
- root = etree.fromstring(generate_html(escape(orig_comments)),
- parser=RECOVER_PARSER)
- jacket = XPath('//h:meta[@name="calibre-content" and @content="jacket"]')
- found = None
- for item in list(self.oeb.spine)[:4]:
- try:
- if jacket(item.data):
- found = item
- break
- except:
- continue
- if found is None:
- item = self.oeb.manifest.add(id, href, guess_type(href)[0], data=root)
- self.oeb.spine.insert(0, item, True)
- else:
- self.log('Found existing book jacket, replacing...')
- found.data = root
+ root = render_jacket(mi, self.opts.output_profile,
+ alt_title=title, alt_tags=tags,
+ alt_comments=comments)
+ id, href = self.oeb.manifest.generate('calibre_jacket', 'jacket.xhtml')
+ item = self.oeb.manifest.add(id, href, guess_type(href)[0], data=root)
+ self.oeb.spine.insert(0, item, True)
+
+ def remove_existing_jacket(self):
+ for x in self.oeb.spine[:4]:
+ if XPath(JACKET_XPATH)(x.data):
+ self.remove_images(x, limit=sys.maxint)
+ self.oeb.manifest.remove(x)
+ self.log('Removed existing jacket')
+ break
def __call__(self, oeb, opts, metadata):
+ '''
+ Add metadata in jacket.xhtml if specified in opts
+ If not specified, remove previous jacket instance
+ '''
self.oeb, self.opts, self.log = oeb, opts, oeb.log
+ self.remove_existing_jacket()
if opts.remove_first_image:
self.remove_first_image()
if opts.insert_metadata:
self.insert_metadata(metadata)
+
+# Render Jacket {{{
+
+def get_rating(rating):
+ ans = ''
+ try:
+ num = float(rating)/2
+ except:
+ return ans
+ num = max(0, num)
+ num = min(num, 5)
+ if num < 1:
+ return ans
+
+ ans = u'\u2605' * int(num)
+ return ans
+
+
+def render_jacket(mi, output_profile,
+ alt_title=_('Unknown'), alt_tags=[], alt_comments=''):
+ css = P('jacket/stylesheet.css', data=True).decode('utf-8')
+
+ try:
+ title_str = mi.title if mi.title else alt_title
+ except:
+ title_str = _('Unknown')
+ title = '%s' % (escape(title_str))
+
+ series = escape(mi.series if mi.series else '')
+ if mi.series and mi.series_index is not None:
+ series += escape(' [%s]'%mi.format_series_index())
+ if not mi.series:
+ series = ''
+
+ try:
+ pubdate = strftime(u'%Y', mi.pubdate.timetuple())
+ except:
+ pubdate = ''
+
+ rating = get_rating(mi.rating)
+
+ tags = mi.tags if mi.tags else alt_tags
+ if tags:
+ tags = output_profile.tags_to_string(tags)
+ else:
+ tags = ''
+
+ comments = mi.comments if mi.comments else alt_comments
+ comments = comments.strip()
+ orig_comments = comments
+ if comments:
+ comments = comments_to_html(comments)
+
+ def generate_html(comments):
+ args = dict(xmlns=XHTML_NS,
+ title_str=title_str,
+ css=css,
+ title=title,
+ pubdate_label=_('Published'), pubdate=pubdate,
+ series_label=_('Series'), series=series,
+ rating_label=_('Rating'), rating=rating,
+ tags_label=_('Tags'), tags=tags,
+ comments=comments,
+ footer=''
+ )
+
+ generated_html = P('jacket/template.xhtml',
+ data=True).decode('utf-8').format(**args)
+
+ # Post-process the generated html to strip out empty header items
+ soup = BeautifulSoup(generated_html)
+ if not series:
+ series_tag = soup.find('tr', attrs={'class':'cbj_series'})
+ series_tag.extract()
+ if not rating:
+ rating_tag = soup.find('tr', attrs={'class':'cbj_rating'})
+ rating_tag.extract()
+ if not tags:
+ tags_tag = soup.find('tr', attrs={'class':'cbj_tags'})
+ tags_tag.extract()
+ if not pubdate:
+ pubdate_tag = soup.find('tr', attrs={'class':'cbj_pubdate'})
+ pubdate_tag.extract()
+ if output_profile.short_name != 'kindle':
+ hr_tag = soup.find('hr', attrs={'class':'cbj_kindle_banner_hr'})
+ hr_tag.extract()
+
+ return soup.renderContents(None)
+
+ from calibre.ebooks.oeb.base import RECOVER_PARSER
+
+ try:
+ root = etree.fromstring(generate_html(comments), parser=RECOVER_PARSER)
+ except:
+ try:
+ root = etree.fromstring(generate_html(escape(orig_comments)),
+ parser=RECOVER_PARSER)
+ except:
+ root = etree.fromstring(generate_html(''),
+ parser=RECOVER_PARSER)
+ return root
+
+# }}}
+
+def linearize_jacket(oeb):
+ for x in oeb.spine[:4]:
+ if XPath(JACKET_XPATH)(x.data):
+ for e in XPath('//h:table|//h:tr|//h:th')(x.data):
+ e.tag = XHTML('div')
+ for e in XPath('//h:td')(x.data):
+ e.tag = XHTML('span')
+ break
+
diff --git a/src/calibre/gui2/tag_view.py b/src/calibre/gui2/tag_view.py
index 519d533ff6..6c50a71b92 100644
--- a/src/calibre/gui2/tag_view.py
+++ b/src/calibre/gui2/tag_view.py
@@ -376,7 +376,7 @@ class TagsModel(QAbstractItemModel): # {{{
'series' : QIcon(I('series.png')),
'formats' : QIcon(I('book.png')),
'publisher' : QIcon(I('publisher.png')),
- 'rating' : QIcon(I('star.png')),
+ 'rating' : QIcon(I('rating.png')),
'news' : QIcon(I('news.png')),
'tags' : QIcon(I('tags.png')),
':custom' : QIcon(I('column.png')),
diff --git a/src/calibre/library/catalog.py b/src/calibre/library/catalog.py
index bd2160aff1..ef7569bd88 100644
--- a/src/calibre/library/catalog.py
+++ b/src/calibre/library/catalog.py
@@ -2523,6 +2523,10 @@ class EPUB_MOBI(CatalogPlugin):
# Fetch the database as a dictionary
self.booksBySeries = self.plugin.search_sort_db(self.db, self.opts)
+ if not self.booksBySeries:
+ self.opts.generate_series = False
+ self.opts.log(" no series found in selected books, cancelling series generation")
+ return
friendly_name = "Series"
diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py
index 36f11469f0..8a2979f122 100644
--- a/src/calibre/library/database2.py
+++ b/src/calibre/library/database2.py
@@ -604,7 +604,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
return identical_book_ids
def has_cover(self, index, index_is_id=False):
- id = index if index_is_id else self.id(index)
+ id = index if index_is_id else self.id(index)
path = os.path.join(self.library_path, self.path(id, index_is_id=True), 'cover.jpg')
return os.access(path, os.R_OK)
diff --git a/src/calibre/utils/magick/draw.py b/src/calibre/utils/magick/draw.py
index 82a0237b8d..301bf9912a 100644
--- a/src/calibre/utils/magick/draw.py
+++ b/src/calibre/utils/magick/draw.py
@@ -5,12 +5,14 @@ __license__ = 'GPL v3'
__copyright__ = '2010, Kovid Goyal '
__docformat__ = 'restructuredtext en'
+import os
from calibre.utils.magick import Image, DrawingWand, create_canvas
from calibre.constants import __appname__, __version__
from calibre import fit_image
-def save_cover_data_to(data, path, bgcolor='white', resize_to=None):
+def save_cover_data_to(data, path, bgcolor='white', resize_to=None,
+ return_data=False):
'''
Saves image in data to path, in the format specified by the path
extension. Composes the image onto a blank canvas so as to
@@ -22,6 +24,8 @@ def save_cover_data_to(data, path, bgcolor='white', resize_to=None):
img.size = (resize_to[0], resize_to[1])
canvas = create_canvas(img.size[0], img.size[1], bgcolor)
canvas.compose(img)
+ if return_data:
+ return canvas.export(os.path.splitext(path)[1][1:])
canvas.save(path)
def thumbnail(data, width=120, height=120, bgcolor='white', fmt='jpg'):