diff --git a/src/calibre/ebooks/mobi/output.py b/src/calibre/ebooks/mobi/output.py
index 7e4643dac1..e3a1da34cc 100644
--- a/src/calibre/ebooks/mobi/output.py
+++ b/src/calibre/ebooks/mobi/output.py
@@ -50,7 +50,7 @@ class MOBIOutput(OutputFormatPlugin):
def check_for_masthead(self):
found = 'masthead' in self.oeb.guide
if not found:
- self.oeb.log.debug('No masthead found, generating default one...')
+ self.oeb.log.debug('No masthead found in manifest, generating default mastheadImage...')
try:
from PIL import Image as PILImage
PILImage
@@ -65,6 +65,9 @@ class MOBIOutput(OutputFormatPlugin):
id, href = self.oeb.manifest.generate('masthead', 'masthead')
self.oeb.manifest.add(id, href, 'image/gif', data=raw)
self.oeb.guide.add('masthead', 'Masthead Image', href)
+ else:
+ self.oeb.log.debug('Using mastheadImage supplied in manifest...')
+
def dump_toc(self, toc) :
self.log( "\n >>> TOC contents <<<")
diff --git a/src/calibre/gui2/catalog/catalog_epub_mobi.ui b/src/calibre/gui2/catalog/catalog_epub_mobi.ui
index 858aec429f..044ecdaaec 100644
--- a/src/calibre/gui2/catalog/catalog_epub_mobi.ui
+++ b/src/calibre/gui2/catalog/catalog_epub_mobi.ui
@@ -14,19 +14,6 @@
Form
- -
-
-
- Tags to exclude as genres (regex):
-
-
- Qt::LogText
-
-
- true
-
-
-
-
@@ -37,7 +24,7 @@
-
-
+
@@ -51,7 +38,7 @@
-
-
+
@@ -65,18 +52,67 @@
-
-
+
- -
+
-
+
+
+ Sort numbers as text
+
+
+
+ -
-
+
- -
+
-
+
+
+ Regex pattern describing tags to exclude as genres:
+
+
+ Qt::LogText
+
+
+ true
+
+
+
+ -
+
+
+
+ 14
+ 75
+ true
+
+
+
+ Special marker tags for catalog generation
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+ Regex tips:
+- The default regex of '\[[\w]*\]' ignores tags of the form '[tag]', e.g., '[Amazon Freebie]'
+- A regex of '.' ignores all tags, generating no genre categories in the catalog
+
+
+ true
+
+
+
+ -
Qt::Vertical
@@ -89,13 +125,6 @@
- -
-
-
- Sort numbers as text
-
-
-
diff --git a/src/calibre/gui2/dialogs/catalog.ui b/src/calibre/gui2/dialogs/catalog.ui
index c18e08ef65..3d62f36e85 100644
--- a/src/calibre/gui2/dialogs/catalog.ui
+++ b/src/calibre/gui2/dialogs/catalog.ui
@@ -107,7 +107,7 @@
12
12
- 205
+ 301
17
diff --git a/src/calibre/library/catalog.py b/src/calibre/library/catalog.py
index 98ab27c3c7..624726cd7c 100644
--- a/src/calibre/library/catalog.py
+++ b/src/calibre/library/catalog.py
@@ -1773,7 +1773,8 @@ class EPUB_MOBI(CatalogPlugin):
self.playOrder += 1
navLabelTag = Tag(soup, 'navLabel')
textTag = Tag(soup, 'text')
- textTag.insert(0, NavigableString("Titles beginning with %s" % (title_letters[i])))
+ textTag.insert(0, NavigableString(u"Titles beginning with %s" % \
+ (title_letters[i] if len(title_letters[i])>1 else "'" + title_letters[i] + "'")))
navLabelTag.insert(0, textTag)
navPointByLetterTag.insert(0,navLabelTag)
contentTag = Tag(soup, 'content')
diff --git a/src/calibre/web/feeds/news.py b/src/calibre/web/feeds/news.py
index e76d8aaa8f..65f6135b8e 100644
--- a/src/calibre/web/feeds/news.py
+++ b/src/calibre/web/feeds/news.py
@@ -274,6 +274,10 @@ class BasicNewsRecipe(Recipe):
}
'''
+ #: By default, calibre will use a default image for the masthead (Kindle only).
+ #: Override this in your recipe to provide a url to use as a masthead.
+ masthead_url = None
+
#: Set to a non empty string to disable this recipe
#: The string will be used as the disabled message
recipe_disabled = None
@@ -294,6 +298,17 @@ class BasicNewsRecipe(Recipe):
'''
return getattr(self, 'cover_url', None)
+ def get_masthead_url(self):
+ '''
+ Return a :term:`URL` to the masthead image for this issue or `None`.
+ By default it returns the value of the member `self.masthead_url` which
+ is normally `None`. If you want your recipe to download a masthead for the e-book
+ override this method in your subclass, or set the member variable `self.masthead_url`
+ before this method is called.
+ Masthead images are used in Kindle MOBI files.
+ '''
+ return getattr(self, 'masthead_url', None)
+
def get_feeds(self):
'''
Return a list of :term:`RSS` feeds to fetch for this profile. Each element of the list
@@ -745,6 +760,18 @@ class BasicNewsRecipe(Recipe):
self.report_progress(0, _('Trying to download cover...'))
self.download_cover()
+ self.report_progress(0, _('Generating masthead...'))
+ try:
+ murl = self.get_masthead_url()
+ except:
+ self.log.exception('Failed to get masthead url')
+ murl = None
+ if murl is not None:
+ self.download_masthead(murl)
+ else:
+ self.masthead_path = os.path.join(self.output_dir, 'mastheadImage.jpg')
+ self.default_masthead_image(self.masthead_path)
+
if self.test:
feeds = feeds[:2]
self.has_single_feed = len(feeds) == 1
@@ -861,6 +888,33 @@ class BasicNewsRecipe(Recipe):
self.log.exception('Failed to download cover')
self.cover_path = None
+ def _download_masthead(self, mu):
+ ext = mu.rpartition('.')[-1]
+ if '?' in ext:
+ ext = ''
+ ext = ext.lower() if ext else 'jpg'
+ mpath = os.path.join(self.output_dir, 'masthead_source.'+ext)
+ outfile = os.path.join(self.output_dir, 'mastheadImage.jpg')
+ if os.access(mu, os.R_OK):
+ with open(mpath, 'wb') as mfile:
+ mfile.write(open(mu, 'rb').read())
+ else:
+ with nested(open(mpath, 'wb'), closing(self.browser.open(mu))) as (mfile, r):
+ mfile.write(r.read())
+ self.report_progress(1, _('Masthead image downloaded'))
+ self.prepare_masthead_image(mpath, outfile)
+ self.masthead_path = outfile
+ if os.path.exists(mpath):
+ os.remove(mpath)
+
+
+ def download_masthead(self, url):
+ try:
+ self._download_masthead(url)
+ except:
+ self.log.exception('Failed to download masthead')
+
+
def default_cover(self, cover_file):
'''
Create a generic cover for recipes that dont have a cover
@@ -964,7 +1018,7 @@ class BasicNewsRecipe(Recipe):
raise IOError('Failed to read image from: %s: %s'
%(path_to_image, msg))
pw.PixelSetColor(p, 'white')
- width, height = pw.MagickGetImageWidth(img),pw.MagickGetImageHeight(img)[1:]
+ width, height = pw.MagickGetImageWidth(img),pw.MagickGetImageHeight(img)
scaled, nwidth, nheight = fit_image(width, height, 600, 100)
if not pw.MagickNewImage(img2, width, height, p):
raise RuntimeError('Out of memory')
@@ -988,7 +1042,6 @@ class BasicNewsRecipe(Recipe):
for x in (img, img2, frame):
pw.DestroyMagickWand(x)
-
def create_opf(self, feeds, dir=None):
if dir is None:
dir = self.output_dir
@@ -1003,11 +1056,22 @@ class BasicNewsRecipe(Recipe):
mi.pubdate = datetime.now()
opf_path = os.path.join(dir, 'index.opf')
ncx_path = os.path.join(dir, 'index.ncx')
+
opf = OPFCreator(dir, mi)
+ # Add mastheadImage entry to section
+ mp = getattr(self, 'masthead_path', None)
+ if mp is not None:
+ from calibre.ebooks.metadata.opf2 import Guide
+ ref = Guide.Reference(os.path.basename(self.masthead_path), os.getcwdu())
+ ref.type = 'masthead'
+ ref.title = 'Masthead Image'
+ opf.guide.append(ref)
manifest = [os.path.join(dir, 'feed_%d'%i) for i in range(len(feeds))]
manifest.append(os.path.join(dir, 'index.html'))
manifest.append(os.path.join(dir, 'index.ncx'))
+
+ # Get cover
cpath = getattr(self, 'cover_path', None)
if cpath is None:
pf = open(os.path.join(dir, 'cover.jpg'), 'wb')
@@ -1016,10 +1080,18 @@ class BasicNewsRecipe(Recipe):
if cpath is not None and os.access(cpath, os.R_OK):
opf.cover = cpath
manifest.append(cpath)
+
+ # Get masthead
+ mpath = getattr(self, 'masthead_path', None)
+ if mpath is not None and os.access(mpath, os.R_OK):
+ manifest.append(mpath)
+
opf.create_manifest_from_files_in(manifest)
for mani in opf.manifest:
if mani.path.endswith('.ncx'):
mani.id = 'ncx'
+ if mani.path.endswith('mastheadImage.jpg'):
+ mani.id = 'masthead-image'
entries = ['index.html']
toc = TOC(base_path=dir)