Use Qt to draw catalog covers and mastheads as well

This commit is contained in:
Kovid Goyal 2014-09-24 09:46:23 +05:30
parent 9963e4263e
commit 88df6cbb3d
5 changed files with 77 additions and 66 deletions

View File

@ -384,14 +384,6 @@ maximum_resort_levels = 5
# the fields that are being displayed.
sort_dates_using_visible_fields = False
#: Specify which font to use when generating a default cover or masthead
# Absolute path to .ttf font files to use as the fonts for the title, author
# and footer when generating a default cover or masthead image. Useful if the
# default font (Liberation Serif) does not contain glyphs for the language of
# the books in your library.
generate_cover_title_font = None
generate_cover_foot_font = None
#: Fuzz value for trimming covers
# The value used for the fuzz distance when trimming a cover.
# Colors within this distance are considered equal.

View File

@ -8,8 +8,7 @@ from various formats.
'''
import traceback, os, re
from cStringIO import StringIO
from calibre import CurrentDir, force_unicode
from calibre import CurrentDir
class ConversionError(Exception):
@ -181,7 +180,6 @@ def normalize(x):
def calibre_cover(title, author_string, series_string=None,
output_format='jpg', title_size=46, author_size=36, logo_path=None):
from calibre.utils.config_base import tweaks
title = normalize(title)
author_string = normalize(author_string)
series_string = normalize(series_string)
@ -189,9 +187,7 @@ def calibre_cover(title, author_string, series_string=None,
import regex
pat = regex.compile(ur'\p{Cf}+', flags=regex.VERSION1) # remove non-printing chars like the soft hyphen
text = pat.sub(u'', title + author_string + (series_string or u''))
font_path = tweaks['generate_cover_title_font']
if font_path is None:
font_path = P('fonts/liberation/LiberationSerif-Bold.ttf')
font_path = P('fonts/liberation/LiberationSerif-Bold.ttf')
from calibre.utils.fonts.utils import get_font_for_text
font = open(font_path, 'rb').read()
@ -261,48 +257,8 @@ def unit_convert(value, base, font, dpi, body_font_size=12):
def generate_masthead(title, output_path=None, width=600, height=60):
from calibre.ebooks.conversion.config import load_defaults
from calibre.utils.config import tweaks
fp = tweaks['generate_cover_title_font']
if not fp:
fp = P('fonts/liberation/LiberationSerif-Bold.ttf')
font_path = default_font = fp
recs = load_defaults('mobi_output')
masthead_font_family = recs.get('masthead_font', 'Default')
if masthead_font_family != 'Default':
from calibre.utils.fonts.scanner import font_scanner, NoFonts
try:
faces = font_scanner.fonts_for_family(masthead_font_family)
except NoFonts:
faces = []
if faces:
font_path = faces[0]['path']
if not font_path or not os.access(font_path, os.R_OK):
font_path = default_font
try:
from PIL import Image, ImageDraw, ImageFont
Image, ImageDraw, ImageFont
except ImportError:
import Image, ImageDraw, ImageFont
img = Image.new('RGB', (width, height), 'white')
draw = ImageDraw.Draw(img)
try:
font = ImageFont.truetype(font_path, 48, encoding='unic')
except:
font = ImageFont.truetype(default_font, 48, encoding='unic')
text = force_unicode(title)
width, height = draw.textsize(text, font=font)
left = max(int((width - width)/2.), 0)
top = max(int((height - height)/2.), 0)
draw.text((left, top), text, fill=(0,0,0), font=font)
if output_path is None:
f = StringIO()
img.save(f, 'JPEG')
return f.getvalue()
else:
with open(output_path, 'wb') as f:
img.save(f, 'JPEG')
masthead_font_family = recs.get('masthead_font', None)
from calibre.ebooks.covers import generate_masthead
return generate_masthead(title, output_path=output_path, width=width, height=height, font_family=masthead_font_family)

View File

@ -19,7 +19,7 @@ from PyQt5.Qt import (
QPainterPath, QPen, QRectF
)
from calibre import force_unicode
from calibre import force_unicode, fit_image
from calibre.constants import __appname__, __version__
from calibre.ebooks.metadata import fmt_sidx
from calibre.ebooks.metadata.book.base import Metadata
@ -517,10 +517,76 @@ def create_cover(title, authors, series=None, series_index=1, prefs=None, as_qim
prefs or cprefs, title_template=d['title_template'], subtitle_template=d['subtitle_template'], footer_template=d['footer_template'])
return generate_cover(mi, prefs=prefs, as_qimage=as_qimage)
def calibre_cover2(title, author_string='', series_string='', prefs=None, as_qimage=False):
init_environment()
title, subtitle, footer = '<b>' + escape_formatting(title), '<i>' + escape_formatting(series_string), '<b>' + escape_formatting(author_string)
prefs = prefs or cprefs
prefs = {k:prefs.get(k) for k in cprefs.defaults}
scale = 800. / prefs['cover_height']
scale_cover(prefs, scale)
prefs = Prefs(**prefs)
img = QImage(prefs.cover_width, prefs.cover_height, QImage.Format_ARGB32)
img.fill(Qt.white)
# colors = to_theme('ffffff ffffff 000000 000000')
color_theme = theme_to_colors(fallback_colors)
class CalibeLogoStyle(Style):
NAME = GUI_NAME = 'calibre'
def __call__(self, painter, rect, color_theme, title_block, subtitle_block, footer_block):
top = title_block.position.y + 10
extra_spacing = subtitle_block.line_spacing // 2 if subtitle_block.line_spacing else title_block.line_spacing // 3
height = title_block.height + subtitle_block.height + extra_spacing + title_block.leading
top += height + 25
bottom = footer_block.position.y - 50
logo = QImage(I('library.png'))
pwidth, pheight = rect.width(), bottom - top
scaled, width, height = fit_image(logo.width(), logo.height(), pwidth, pheight)
x, y = (pwidth - width) // 2, (pheight - height) // 2
rect = QRect(x, top + y, width, height)
painter.setRenderHint(QPainter.SmoothPixmapTransform)
painter.drawImage(rect, logo)
return self.ccolor1, self.ccolor1, self.ccolor1
style = CalibeLogoStyle(color_theme, prefs)
title_block, subtitle_block, footer_block = layout_text(
prefs, img, title, subtitle, footer, img.height() // 3, style)
p = QPainter(img)
rect = QRect(0, 0, img.width(), img.height())
colors = style(p, rect, color_theme, title_block, subtitle_block, footer_block)
for block, color in zip((title_block, subtitle_block, footer_block), colors):
p.setPen(color)
block.draw(p)
p.end()
img.setText('Generated cover', '%s %s' % (__appname__, __version__))
if as_qimage:
return img
return pixmap_to_data(img)
def scale_cover(prefs, scale):
for x in ('cover_width', 'cover_height', 'title_font_size', 'subtitle_font_size', 'footer_font_size'):
prefs[x] = int(scale * prefs[x])
def generate_masthead(title, output_path=None, width=600, height=60, as_qimage=False, font_family=None):
init_environment()
font_family = font_family or cprefs['title_font_family'] or 'Liberation Serif'
img = QImage(width, height, QImage.Format_ARGB32)
img.fill(Qt.white)
p = QPainter(img)
f = QFont(font_family)
f.setPixelSize((height * 3) // 4), f.setBold(True)
p.setFont(f)
p.drawText(img.rect(), Qt.AlignLeft | Qt.AlignVCenter, sanitize(title))
p.end()
if as_qimage:
return img
data = pixmap_to_data(img)
if output_path is None:
return data
with open(output_path, 'wb') as f:
f.write(data)
def test(scale=0.25):
from PyQt5.Qt import QLabel, QApplication, QPixmap, QMainWindow, QWidget, QScrollArea, QGridLayout
app = QApplication([])
mi = Metadata('xxx', ['Kovid Goyal', 'John Q. Doe', 'Author'])
mi = Metadata('xxx', ['Kovid Goyal', 'John & Doe', 'Author'])
mi.series = 'A series of styles'
m = QMainWindow()
sa = QScrollArea(m)
@ -534,8 +600,7 @@ def test(scale=0.25):
mi.series_index = c + 1
mi.title = 'An algorithmic cover [%s]' % color
prefs = override_prefs(cprefs, override_color_theme=color, override_style=style)
for x in ('cover_width', 'cover_height', 'title_font_size', 'subtitle_font_size', 'footer_font_size'):
prefs[x] = int(scale * prefs[x])
scale_cover(prefs, scale)
img = generate_cover(mi, prefs=prefs, as_qimage=True)
la = QLabel()
la.setPixmap(QPixmap.fromImage(img))

View File

@ -13,7 +13,6 @@ from collections import namedtuple
from calibre import strftime
from calibre.customize import CatalogPlugin
from calibre.customize.conversion import OptionRecommendation, DummyReporter
from calibre.ebooks import calibre_cover
from calibre.library import current_library_name
from calibre.library.catalogs import AuthorSortMismatchException, EmptyCatalogException
from calibre.ptempfile import PersistentTemporaryFile
@ -463,9 +462,10 @@ class EPUB_MOBI(CatalogPlugin):
recommendations.append(('cover', cpath, OptionRecommendation.HIGH))
log.info("using existing catalog cover")
else:
from calibre.ebooks.covers import calibre_cover2
log.info("replacing catalog cover")
new_cover_path = PersistentTemporaryFile(suffix='.jpg')
new_cover = calibre_cover(opts.catalog_title.replace('"', '\\"'), 'calibre')
new_cover = calibre_cover2(opts.catalog_title, 'calibre')
new_cover_path.write(new_cover)
new_cover_path.close()
recommendations.append(('cover', new_cover_path.name, OptionRecommendation.HIGH))

View File

@ -273,9 +273,7 @@ def create_cover_page(top_lines, logo_path, width=590, height=750,
bottom += line.bottom_margin
bottom -= top_lines[-1].bottom_margin
foot_font = tweaks['generate_cover_foot_font']
if not foot_font:
foot_font = P('fonts/liberation/LiberationMono-Regular.ttf')
foot_font = P('fonts/liberation/LiberationMono-Regular.ttf')
vanity = create_text_arc(__appname__ + ' ' + __version__, 24,
font=foot_font, bgcolor='#00000000')
lwidth, lheight = vanity.size