mirror of
				https://github.com/kovidgoyal/calibre.git
				synced 2025-10-31 02:27:01 -04:00 
			
		
		
		
	MOBI Output: Make font used for generated masthead images user customizable. Various catalog fixes.
This commit is contained in:
		
						commit
						bae592ae3a
					
				| @ -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 | ||||||
|  | |||||||
| @ -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 | ||||||
|  | |||||||
| @ -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/> | ||||||
|  | |||||||
| @ -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: | ||||||
|  | |||||||
| @ -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) | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user