MOBI Output: Make font used for generated masthead images user customizable. Various catalog fixes.

This commit is contained in:
Kovid Goyal 2010-02-02 14:59:19 -07:00
commit bae592ae3a
5 changed files with 150 additions and 46 deletions

View File

@ -42,6 +42,7 @@ def gui_catalog(fmt, title, dbspec, ids, out_file_name, sync, fmt_options,
opts, args = parser.parse_args() opts, args = parser.parse_args()
# Populate opts # Populate opts
# opts.gui_search_text = something
opts.catalog_title = title opts.catalog_title = title
opts.ids = ids opts.ids = ids
opts.search_text = None opts.search_text = None

View File

@ -6,9 +6,14 @@ __license__ = 'GPL v3'
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>' __copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
from PyQt4.Qt import Qt
from calibre.gui2.convert.mobi_output_ui import Ui_Form from calibre.gui2.convert.mobi_output_ui import Ui_Form
from calibre.gui2.convert import Widget from calibre.gui2.convert import Widget
from calibre.gui2.widgets import FontFamilyModel
from calibre.utils.fonts import fontconfig
font_family_model = None
class PluginWidget(Widget, Ui_Form): class PluginWidget(Widget, Ui_Form):
@ -19,8 +24,35 @@ class PluginWidget(Widget, Ui_Form):
def __init__(self, parent, get_option, get_help, db=None, book_id=None): def __init__(self, parent, get_option, get_help, db=None, book_id=None):
Widget.__init__(self, parent, 'mobi_output', Widget.__init__(self, parent, 'mobi_output',
['prefer_author_sort', 'rescale_images', 'toc_title', ['prefer_author_sort', 'rescale_images', 'toc_title',
'dont_compress', 'no_inline_toc'] 'dont_compress', 'no_inline_toc', 'masthead_font']
) )
self.db, self.book_id = db, book_id self.db, self.book_id = db, book_id
global font_family_model
if font_family_model is None:
font_family_model = FontFamilyModel()
try:
font_family_model.families = fontconfig.find_font_families(allowed_extensions=['ttf'])
except:
import traceback
font_family_model.families = []
print 'WARNING: Could not load fonts'
traceback.print_exc()
font_family_model.families.sort()
font_family_model.families[:0] = [_('Default')]
self.font_family_model = font_family_model
self.opt_masthead_font.setModel(self.font_family_model)
self.initialize_options(get_option, get_help, db, book_id) self.initialize_options(get_option, get_help, db, book_id)
def set_value_handler(self, g, val):
if unicode(g.objectName()) in 'opt_masthead_font':
idx = -1
if val:
idx = g.findText(val, Qt.MatchFixedString)
if idx < 0:
idx = 0
g.setCurrentIndex(idx)
return True
return False

View File

@ -6,7 +6,7 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>400</width> <width>421</width>
<height>300</height> <height>300</height>
</rect> </rect>
</property> </property>
@ -41,19 +41,6 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="5" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="4" column="0"> <item row="4" column="0">
<widget class="QCheckBox" name="opt_dont_compress"> <widget class="QCheckBox" name="opt_dont_compress">
<property name="text"> <property name="text">
@ -68,6 +55,51 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="5" column="0" colspan="2">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Kindle options</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Masthead font:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="opt_masthead_font"/>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>55</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item row="6" column="0">
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout> </layout>
</widget> </widget>
<resources/> <resources/>

View File

@ -403,10 +403,15 @@ class EPUB_MOBI(CatalogPlugin):
right = EPUB_MOBI.NumberToText(strings[1]).text right = EPUB_MOBI.NumberToText(strings[1]).text
self.text = '%s-%s' % (left, right) self.text = '%s-%s' % (left, right)
# Test for $xx,xxx
elif re.search('[$,]', self.number):
self.number_as_float = re.sub('[$,]','',self.number)
self.text = EPUB_MOBI.NumberToText(self.number_as_float).text
# Test for comma # Test for comma
elif re.search(',', self.number): elif re.search(',', self.number):
self.number_as_float = re.sub(',','',self.number) self.number_as_float = re.sub(',','',self.number)
self.text = EPUB_MOBI.NumberToText(self.number.replace(',','')).text self.text = EPUB_MOBI.NumberToText(self.number_as_float).text
# Test for hybrid e.g., 'K2' # Test for hybrid e.g., 'K2'
elif re.search('[\D]+', self.number): elif re.search('[\D]+', self.number):
@ -482,11 +487,6 @@ class EPUB_MOBI(CatalogPlugin):
catalog.createDirectoryStructure() catalog.createDirectoryStructure()
catalog.copyResources() catalog.copyResources()
catalog.buildSources() catalog.buildSources()
- To do:
*** generateThumbnails() creates a default book image from book.svg, but the background
is black instead of white. This needs to be fixed (approx line #1418)
''' '''
# Number of discrete steps to catalog creation # Number of discrete steps to catalog creation
@ -811,7 +811,7 @@ class EPUB_MOBI(CatalogPlugin):
# If failure, default mastheadImage.gif should still be in place # If failure, default mastheadImage.gif should still be in place
if self.generateForKindle: if self.generateForKindle:
try: try:
self.generate_masthead_image(os.path.join(self.catalogPath, self.generateMastheadImage(os.path.join(self.catalogPath,
'images/mastheadImage.gif')) 'images/mastheadImage.gif'))
except: except:
pass pass
@ -831,11 +831,14 @@ class EPUB_MOBI(CatalogPlugin):
# Merge opts.exclude_tag with opts.search_text # Merge opts.exclude_tag with opts.search_text
# What if no exclude tags? # What if no exclude tags?
exclude_tags = self.opts.exclude_tags.split(',') empty_exclude_tags = False if len(self.opts.exclude_tags) else True
search_terms = [] search_phrase = ''
for tag in exclude_tags: if not empty_exclude_tags:
search_terms.append("tag:%s" % tag) exclude_tags = self.opts.exclude_tags.split(',')
search_phrase = "not (%s)" % " or ".join(search_terms) search_terms = []
for tag in exclude_tags:
search_terms.append("tag:%s" % tag)
search_phrase = "not (%s)" % " or ".join(search_terms)
# If a list of ids are provided, don't use search_text # If a list of ids are provided, don't use search_text
if self.opts.ids: if self.opts.ids:
@ -846,6 +849,7 @@ class EPUB_MOBI(CatalogPlugin):
else: else:
self.opts.search_text = search_phrase self.opts.search_text = search_phrase
#print "fetchBooksByTitle(): opts.search_text: %s" % self.opts.search_text
# Fetch the database as a dictionary # Fetch the database as a dictionary
data = self.plugin.search_sort_db(self.db, self.opts) data = self.plugin.search_sort_db(self.db, self.opts)
@ -1455,7 +1459,6 @@ class EPUB_MOBI(CatalogPlugin):
self.updateProgressFullStep("'Genres'") self.updateProgressFullStep("'Genres'")
# filtered_tags = {friendly:normalized, }
self.genre_tags_dict = self.filterDbTags(self.db.all_tags()) self.genre_tags_dict = self.filterDbTags(self.db.all_tags())
# Extract books matching filtered_tags # Extract books matching filtered_tags
@ -2202,11 +2205,11 @@ class EPUB_MOBI(CatalogPlugin):
# GwR *** Can this be optimized? # GwR *** Can this be optimized?
normalized_tag = None normalized_tag = None
for genre_tag in self.genre_tags_dict: for friendly_tag in self.genre_tags_dict:
if self.genre_tags_dict[genre_tag] == genre['tag']: if self.genre_tags_dict[friendly_tag] == genre['tag']:
normalized_tag = self.genre_tags_dict[genre_tag] normalized_tag = self.genre_tags_dict[friendly_tag]
break break
textTag.insert(0, self.formatNCXText(NavigableString(genre_tag))) textTag.insert(0, self.formatNCXText(NavigableString(friendly_tag)))
navLabelTag.insert(0,textTag) navLabelTag.insert(0,textTag)
navPointVolumeTag.insert(0,navLabelTag) navPointVolumeTag.insert(0,navLabelTag)
contentTag = Tag(ncx_soup, "content") contentTag = Tag(ncx_soup, "content")
@ -2312,14 +2315,6 @@ class EPUB_MOBI(CatalogPlugin):
if not os.path.isdir(images_path): if not os.path.isdir(images_path):
os.makedirs(images_path) os.makedirs(images_path)
def getMarkerTags(self):
''' Return a list of special marker tags to be excluded from genre list '''
markerTags = []
markerTags.extend(self.opts.exclude_tags.split(','))
markerTags.extend(self.opts.note_tag.split(','))
markerTags.extend(self.opts.read_tag.split(','))
return markerTags
def filterDbTags(self, tags): def filterDbTags(self, tags):
# Remove the special marker tags from the database's tag list, # Remove the special marker tags from the database's tag list,
# return sorted list of normalized genre tags # return sorted list of normalized genre tags
@ -2337,7 +2332,6 @@ class EPUB_MOBI(CatalogPlugin):
normalized_tags.append(re.sub('\W','',tag).lower()) normalized_tags.append(re.sub('\W','',tag).lower())
friendly_tags.append(tag) friendly_tags.append(tag)
genre_tags_dict = dict(zip(friendly_tags,normalized_tags)) genre_tags_dict = dict(zip(friendly_tags,normalized_tags))
# Test for multiple genres resolving to same normalized form # Test for multiple genres resolving to same normalized form
@ -2405,10 +2399,10 @@ class EPUB_MOBI(CatalogPlugin):
btc += 1 btc += 1
# Find the first instance of friendly_tag matching genre # Find the first instance of friendly_tag matching genre
# GwR *** optimize
for friendly_tag in self.genre_tags_dict: for friendly_tag in self.genre_tags_dict:
if self.genre_tags_dict[friendly_tag] == genre: if self.genre_tags_dict[friendly_tag] == genre:
break break
titleTag = body.find(attrs={'class':'title'}) titleTag = body.find(attrs={'class':'title'})
titleTag.insert(0,NavigableString('<b><i>%s</i></b>' % escape(friendly_tag))) titleTag.insert(0,NavigableString('<b><i>%s</i></b>' % escape(friendly_tag)))
@ -2570,8 +2564,22 @@ class EPUB_MOBI(CatalogPlugin):
titleTag.insert(0,escape(NavigableString(title))) titleTag.insert(0,escape(NavigableString(title)))
return soup return soup
def generate_masthead_image(self, out_path): def generateMastheadImage(self, out_path):
font_path = P('fonts/liberation/LiberationSerif-Bold.ttf') from calibre.ebooks.conversion.config import load_defaults
from calibre.utils.fonts import fontconfig
font_path = default_font = P('fonts/liberation/LiberationSerif-Bold.ttf')
recs = load_defaults('mobi_output')
masthead_font_family = recs.get('masthead_font', 'Default')
if masthead_font_family != 'Default':
masthead_font = fontconfig.files_for_family(masthead_font_family)
# Assume 'normal' always in dict, else use default
# {'normal': (path_to_font, friendly name)}
if 'normal' in masthead_font:
font_path = masthead_font['normal'][0]
if not font_path or not os.access(font_path, os.R_OK):
font_path = default_font
MI_WIDTH = 600 MI_WIDTH = 600
MI_HEIGHT = 60 MI_HEIGHT = 60
@ -2584,7 +2592,11 @@ class EPUB_MOBI(CatalogPlugin):
img = Image.new('RGB', (MI_WIDTH, MI_HEIGHT), 'white') img = Image.new('RGB', (MI_WIDTH, MI_HEIGHT), 'white')
draw = ImageDraw.Draw(img) draw = ImageDraw.Draw(img)
font = ImageFont.truetype(font_path, 48) try:
font = ImageFont.truetype(font_path, 48)
except:
self.opts.log.error(" Failed to load user-specifed font '%s'" % font_path)
font = ImageFont.truetype(default_font, 48)
text = self.title.encode('utf-8') text = self.title.encode('utf-8')
width, height = draw.textsize(text, font=font) width, height = draw.textsize(text, font=font)
left = max(int((MI_WIDTH - width)/2.), 0) left = max(int((MI_WIDTH - width)/2.), 0)
@ -2682,6 +2694,14 @@ class EPUB_MOBI(CatalogPlugin):
else: else:
return char return char
def getMarkerTags(self):
''' Return a list of special marker tags to be excluded from genre list '''
markerTags = []
markerTags.extend(self.opts.exclude_tags.split(','))
markerTags.extend(self.opts.note_tag.split(','))
markerTags.extend(self.opts.read_tag.split(','))
return markerTags
def processSpecialTags(self, tags, this_title, opts): def processSpecialTags(self, tags, this_title, opts):
tag_list = [] tag_list = []
for tag in tags: for tag in tags:

View File

@ -989,6 +989,22 @@ class BasicNewsRecipe(Recipe):
MI_HEIGHT = 60 MI_HEIGHT = 60
def default_masthead_image(self, out_path): def default_masthead_image(self, out_path):
from calibre.ebooks.conversion.config import load_defaults
from calibre.utils.fonts import fontconfig
font_path = default_font = P('fonts/liberation/LiberationSerif-Bold.ttf')
recs = load_defaults('mobi_output')
masthead_font_family = recs.get('masthead_font', 'Default')
if masthead_font_family != 'Default':
masthead_font = fontconfig.files_for_family(masthead_font_family)
# Assume 'normal' always in dict, else use default
# {'normal': (path_to_font, friendly name)}
if 'normal' in masthead_font:
font_path = masthead_font['normal'][0]
if not font_path or not os.access(font_path, os.R_OK):
font_path = default_font
try: try:
from PIL import Image, ImageDraw, ImageFont from PIL import Image, ImageDraw, ImageFont
Image, ImageDraw, ImageFont Image, ImageDraw, ImageFont
@ -997,7 +1013,10 @@ class BasicNewsRecipe(Recipe):
img = Image.new('RGB', (self.MI_WIDTH, self.MI_HEIGHT), 'white') img = Image.new('RGB', (self.MI_WIDTH, self.MI_HEIGHT), 'white')
draw = ImageDraw.Draw(img) draw = ImageDraw.Draw(img)
font = ImageFont.truetype(P('fonts/liberation/LiberationSerif-Bold.ttf'), 48) try:
font = ImageFont.truetype(font_path, 48)
except:
font = ImageFont.truetype(default_font, 48)
text = self.get_masthead_title().encode('utf-8') text = self.get_masthead_title().encode('utf-8')
width, height = draw.textsize(text, font=font) width, height = draw.textsize(text, font=font)
left = max(int((self.MI_WIDTH - width)/2.), 0) left = max(int((self.MI_WIDTH - width)/2.), 0)