mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Implement #5959 (generate default cover on request)
This commit is contained in:
parent
cb809e74a8
commit
9aa0fc428d
@ -142,6 +142,21 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
|
|||||||
self.cpixmap = pix
|
self.cpixmap = pix
|
||||||
self.cover_data = cover
|
self.cover_data = cover
|
||||||
|
|
||||||
|
def generate_cover(self, *args):
|
||||||
|
from calibre.utils.magick.draw import create_cover_page, TextLine
|
||||||
|
title = unicode(self.title.text()).strip()
|
||||||
|
author = unicode(self.authors.text()).strip()
|
||||||
|
if not title or not author:
|
||||||
|
return error_dialog(self, _('Specify title and author'),
|
||||||
|
_('You must specify a title and author before generating '
|
||||||
|
'a cover'), show=True)
|
||||||
|
lines = [TextLine(title, 44), TextLine(author, 32)]
|
||||||
|
self.cover_data = create_cover_page(lines, I('library.png'))
|
||||||
|
pix = QPixmap()
|
||||||
|
pix.loadFromData(self.cover_data)
|
||||||
|
self.cover.setPixmap(pix)
|
||||||
|
self.cover_changed = True
|
||||||
|
self.cpixmap = pix
|
||||||
|
|
||||||
def add_format(self, x):
|
def add_format(self, x):
|
||||||
files = choose_files(self, 'add formats dialog',
|
files = choose_files(self, 'add formats dialog',
|
||||||
@ -425,6 +440,7 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
|
|||||||
self.central_widget.tabBar().setVisible(False)
|
self.central_widget.tabBar().setVisible(False)
|
||||||
else:
|
else:
|
||||||
self.create_custom_column_editors()
|
self.create_custom_column_editors()
|
||||||
|
self.generate_cover_button.clicked.connect(self.generate_cover)
|
||||||
|
|
||||||
def create_custom_column_editors(self):
|
def create_custom_column_editors(self):
|
||||||
w = self.central_widget.widget(1)
|
w = self.central_widget.widget(1)
|
||||||
|
@ -653,6 +653,13 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="generate_cover_button">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Generate cover</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
@ -736,15 +743,17 @@
|
|||||||
<tabstop>fetch_metadata_button</tabstop>
|
<tabstop>fetch_metadata_button</tabstop>
|
||||||
<tabstop>add_format_button</tabstop>
|
<tabstop>add_format_button</tabstop>
|
||||||
<tabstop>remove_format_button</tabstop>
|
<tabstop>remove_format_button</tabstop>
|
||||||
|
<tabstop>button_set_cover</tabstop>
|
||||||
|
<tabstop>button_set_metadata</tabstop>
|
||||||
|
<tabstop>formats</tabstop>
|
||||||
<tabstop>cover_path</tabstop>
|
<tabstop>cover_path</tabstop>
|
||||||
<tabstop>cover_button</tabstop>
|
<tabstop>cover_button</tabstop>
|
||||||
<tabstop>reset_cover</tabstop>
|
<tabstop>reset_cover</tabstop>
|
||||||
<tabstop>fetch_cover_button</tabstop>
|
<tabstop>fetch_cover_button</tabstop>
|
||||||
<tabstop>button_set_cover</tabstop>
|
<tabstop>generate_cover_button</tabstop>
|
||||||
<tabstop>formats</tabstop>
|
|
||||||
<tabstop>button_set_metadata</tabstop>
|
|
||||||
<tabstop>button_box</tabstop>
|
|
||||||
<tabstop>scrollArea</tabstop>
|
<tabstop>scrollArea</tabstop>
|
||||||
|
<tabstop>central_widget</tabstop>
|
||||||
|
<tabstop>button_box</tabstop>
|
||||||
</tabstops>
|
</tabstops>
|
||||||
<resources>
|
<resources>
|
||||||
<include location="../../../../resources/images.qrc"/>
|
<include location="../../../../resources/images.qrc"/>
|
||||||
|
@ -7,6 +7,7 @@ __docformat__ = 'restructuredtext en'
|
|||||||
|
|
||||||
|
|
||||||
from calibre.utils.magick import Image, DrawingWand, create_canvas
|
from calibre.utils.magick import Image, DrawingWand, create_canvas
|
||||||
|
from calibre.constants import __appname__, __version__
|
||||||
|
|
||||||
def save_cover_data_to(data, path, bgcolor='white', resize_to=None):
|
def save_cover_data_to(data, path, bgcolor='white', resize_to=None):
|
||||||
'''
|
'''
|
||||||
@ -77,4 +78,87 @@ def create_text_arc(text, font_size, font=None, bgcolor='white'):
|
|||||||
canvas.trim(0)
|
canvas.trim(0)
|
||||||
return canvas
|
return canvas
|
||||||
|
|
||||||
|
def _get_line(img, dw, tokens, line_width):
|
||||||
|
line, rest = tokens, []
|
||||||
|
while True:
|
||||||
|
m = img.font_metrics(dw, ' '.join(line))
|
||||||
|
width, height = m.text_width, m.text_height
|
||||||
|
if width < line_width:
|
||||||
|
return line, rest
|
||||||
|
rest = line[-1:] + rest
|
||||||
|
line = line[:-1]
|
||||||
|
|
||||||
|
def annotate_img(img, dw, left, top, rotate, text,
|
||||||
|
translate_from_top_left=True):
|
||||||
|
if isinstance(text, unicode):
|
||||||
|
text = text.encode('utf-8')
|
||||||
|
if translate_from_top_left:
|
||||||
|
m = img.font_metrics(dw, text)
|
||||||
|
img_width, img_height = img.size
|
||||||
|
left = left - img_width/2. + m.text_width/2.
|
||||||
|
top = top - img_height/2. + m.text_height/2.
|
||||||
|
img.annotate(dw, left, top, rotate, text)
|
||||||
|
|
||||||
|
def draw_centered_line(img, dw, line, top):
|
||||||
|
m = img.font_metrics(dw, line)
|
||||||
|
width, height = m.text_width, m.text_height
|
||||||
|
img_width = img.size[0]
|
||||||
|
left = max(int((img_width - width)/2.), 0)
|
||||||
|
annotate_img(img, dw, left, top, 0, line)
|
||||||
|
return top + height
|
||||||
|
|
||||||
|
def draw_centered_text(img, dw, text, top, margin=10):
|
||||||
|
img_width = img.size[0]
|
||||||
|
tokens = text.split(' ')
|
||||||
|
while tokens:
|
||||||
|
line, tokens = _get_line(img, dw, tokens, img_width-2*margin)
|
||||||
|
if not line:
|
||||||
|
# Could not fit the first token on the line
|
||||||
|
line = tokens[:1]
|
||||||
|
tokens = tokens[1:]
|
||||||
|
bottom = draw_centered_line(img, dw, ' '.join(line), top)
|
||||||
|
top = bottom
|
||||||
|
return top
|
||||||
|
|
||||||
|
class TextLine(object):
|
||||||
|
|
||||||
|
def __init__(self, text, font_size, bottom_margin=30, font_path=None):
|
||||||
|
self.text, self.font_size, = text, font_size
|
||||||
|
self.bottom_margin = bottom_margin
|
||||||
|
self.font_path = font_path
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return u'TextLine:%r:%f'%(self.text, self.font_size)
|
||||||
|
|
||||||
|
|
||||||
|
def create_cover_page(top_lines, logo_path, width=590, height=750,
|
||||||
|
bgcolor='white', output_format='jpg'):
|
||||||
|
'''
|
||||||
|
Create the standard calibre cover page and return it as a byte string in
|
||||||
|
the specified output_format.
|
||||||
|
'''
|
||||||
|
canvas = create_canvas(width, height, bgcolor)
|
||||||
|
|
||||||
|
bottom = 10
|
||||||
|
for line in top_lines:
|
||||||
|
twand = create_text_wand(line.font_size, font_path=line.font_path)
|
||||||
|
bottom = draw_centered_text(canvas, twand, line.text, bottom)
|
||||||
|
bottom += line.bottom_margin
|
||||||
|
bottom -= top_lines[-1].bottom_margin
|
||||||
|
|
||||||
|
vanity = create_text_arc(__appname__ + ' ' + __version__, 24,
|
||||||
|
font=P('fonts/liberation/LiberationMono-Regular.ttf'))
|
||||||
|
lwidth, lheight = vanity.size
|
||||||
|
left = int(max(0, (width - lwidth)/2.))
|
||||||
|
top = height - lheight - 10
|
||||||
|
canvas.compose(vanity, left, top)
|
||||||
|
|
||||||
|
logo = Image()
|
||||||
|
logo.open(logo_path)
|
||||||
|
lwidth, lheight = logo.size
|
||||||
|
left = int(max(0, (width - lwidth)/2.))
|
||||||
|
top = max(int((height - lheight)/2.), bottom+20)
|
||||||
|
canvas.compose(logo, left, top)
|
||||||
|
|
||||||
|
return canvas.export(output_format)
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user