mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-08 10:44:09 -04:00
EPUB Output: Add option to toggle preserving the aspect ratio of the cover. The default has changed to not preserving it. PDF Output: Set the first page to the cover. Fixes #5581 (Economist (payed Edition) title image)
This commit is contained in:
parent
1777036798
commit
2ddd2c1c76
@ -46,8 +46,155 @@ block_level_tags = (
|
|||||||
'ul',
|
'ul',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
class CoverManager(object):
|
||||||
|
|
||||||
class EPUBOutput(OutputFormatPlugin):
|
'''
|
||||||
|
Manage the cover in the output document. Requires the opts object to have
|
||||||
|
the attributes:
|
||||||
|
|
||||||
|
no_svg_cover
|
||||||
|
no_default_epub_cover
|
||||||
|
preserve_cover_aspect_ratio
|
||||||
|
'''
|
||||||
|
|
||||||
|
NONSVG_TITLEPAGE_COVER = '''\
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||||
|
<meta name="calibre:cover" content="true" />
|
||||||
|
<title>Cover</title>
|
||||||
|
<style type="text/css" title="override_css">
|
||||||
|
@page {padding: 0pt; margin:0pt}
|
||||||
|
body { text-align: center; padding:0pt; margin: 0pt; }
|
||||||
|
div { padding:0pt; margin: 0pt; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div>
|
||||||
|
<img src="%s" alt="cover" style="height: 100%%" />
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
'''
|
||||||
|
|
||||||
|
TITLEPAGE_COVER = '''\
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||||
|
<meta name="calibre:cover" content="true" />
|
||||||
|
<title>Cover</title>
|
||||||
|
<style type="text/css" title="override_css">
|
||||||
|
@page {padding: 0pt; margin:0pt}
|
||||||
|
body { text-align: center; padding:0pt; margin: 0pt; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
width="100%%" height="100%%" viewBox="0 0 600 800"
|
||||||
|
preserveAspectRatio="__ar__">
|
||||||
|
<image width="600" height="800" xlink:href="%s"/>
|
||||||
|
</svg>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
'''
|
||||||
|
|
||||||
|
def default_cover(self):
|
||||||
|
'''
|
||||||
|
Create a generic cover for books that dont have a cover
|
||||||
|
'''
|
||||||
|
from calibre.utils.pil_draw import draw_centered_text
|
||||||
|
from calibre.ebooks.metadata import authors_to_string
|
||||||
|
if self.opts.no_default_epub_cover:
|
||||||
|
return None
|
||||||
|
self.log('Generating default cover')
|
||||||
|
m = self.oeb.metadata
|
||||||
|
title = unicode(m.title[0])
|
||||||
|
authors = [unicode(x) for x in m.creator if x.role == 'aut']
|
||||||
|
|
||||||
|
import cStringIO
|
||||||
|
cover_file = cStringIO.StringIO()
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
from PIL import Image, ImageDraw, ImageFont
|
||||||
|
Image, ImageDraw, ImageFont
|
||||||
|
except ImportError:
|
||||||
|
import Image, ImageDraw, ImageFont
|
||||||
|
font_path = P('fonts/liberation/LiberationSerif-Bold.ttf')
|
||||||
|
app = '['+__appname__ +' '+__version__+']'
|
||||||
|
|
||||||
|
COVER_WIDTH, COVER_HEIGHT = 590, 750
|
||||||
|
img = Image.new('RGB', (COVER_WIDTH, COVER_HEIGHT), 'white')
|
||||||
|
draw = ImageDraw.Draw(img)
|
||||||
|
# Title
|
||||||
|
font = ImageFont.truetype(font_path, 44)
|
||||||
|
bottom = draw_centered_text(img, draw, font, title, 15, ysep=9)
|
||||||
|
# Authors
|
||||||
|
bottom += 14
|
||||||
|
font = ImageFont.truetype(font_path, 32)
|
||||||
|
authors = authors_to_string(authors)
|
||||||
|
bottom = draw_centered_text(img, draw, font, authors, bottom, ysep=7)
|
||||||
|
# Vanity
|
||||||
|
font = ImageFont.truetype(font_path, 28)
|
||||||
|
width, height = draw.textsize(app, font=font)
|
||||||
|
left = max(int((COVER_WIDTH - width)/2.), 0)
|
||||||
|
top = COVER_HEIGHT - height - 15
|
||||||
|
draw.text((left, top), app, fill=(0,0,0), font=font)
|
||||||
|
# Logo
|
||||||
|
logo = Image.open(I('library.png'), 'r')
|
||||||
|
width, height = logo.size
|
||||||
|
left = max(int((COVER_WIDTH - width)/2.), 0)
|
||||||
|
top = max(int((COVER_HEIGHT - height)/2.), 0)
|
||||||
|
img.paste(logo, (left, max(bottom, top)))
|
||||||
|
img = img.convert('RGB').convert('P', palette=Image.ADAPTIVE)
|
||||||
|
|
||||||
|
img.convert('RGB').save(cover_file, 'JPEG')
|
||||||
|
cover_file.flush()
|
||||||
|
id, href = self.oeb.manifest.generate('cover_image', 'cover_image.jpg')
|
||||||
|
item = self.oeb.manifest.add(id, href, guess_type('t.jpg')[0],
|
||||||
|
data=cover_file.getvalue())
|
||||||
|
m.clear('cover')
|
||||||
|
m.add('cover', item.id)
|
||||||
|
|
||||||
|
return item.href
|
||||||
|
except:
|
||||||
|
self.log.exception('Failed to generate default cover')
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def insert_cover(self):
|
||||||
|
from calibre.ebooks.oeb.base import urldefrag
|
||||||
|
from calibre import guess_type
|
||||||
|
g, m = self.oeb.guide, self.oeb.manifest
|
||||||
|
item = None
|
||||||
|
ar = 'xMidYMid meet' if self.opts.preserve_cover_aspect_ratio else \
|
||||||
|
'none'
|
||||||
|
svg_template = self.TITLEPAGE_COVER.replace('__ar__', ar)
|
||||||
|
if 'titlepage' not in g:
|
||||||
|
if 'cover' in g:
|
||||||
|
href = g['cover'].href
|
||||||
|
else:
|
||||||
|
href = self.default_cover()
|
||||||
|
if href is not None:
|
||||||
|
templ = self.NONSVG_TITLEPAGE_COVER if self.opts.no_svg_cover \
|
||||||
|
else svg_template
|
||||||
|
tp = templ%unquote(href)
|
||||||
|
id, href = m.generate('titlepage', 'titlepage.xhtml')
|
||||||
|
item = m.add(id, href, guess_type('t.xhtml')[0],
|
||||||
|
data=etree.fromstring(tp))
|
||||||
|
else:
|
||||||
|
item = self.oeb.manifest.hrefs[
|
||||||
|
urldefrag(self.oeb.guide['titlepage'].href)[0]]
|
||||||
|
if item is not None:
|
||||||
|
self.oeb.spine.insert(0, item, True)
|
||||||
|
if 'cover' not in self.oeb.guide.refs:
|
||||||
|
self.oeb.guide.add('cover', 'Title Page', 'a')
|
||||||
|
self.oeb.guide.refs['cover'].href = item.href
|
||||||
|
if 'titlepage' in self.oeb.guide.refs:
|
||||||
|
self.oeb.guide.refs['titlepage'].href = item.href
|
||||||
|
|
||||||
|
|
||||||
|
class EPUBOutput(OutputFormatPlugin, CoverManager):
|
||||||
|
|
||||||
name = 'EPUB Output'
|
name = 'EPUB Output'
|
||||||
author = 'Kovid Goyal'
|
author = 'Kovid Goyal'
|
||||||
@ -92,51 +239,21 @@ class EPUBOutput(OutputFormatPlugin):
|
|||||||
'as a blank page.')
|
'as a blank page.')
|
||||||
),
|
),
|
||||||
|
|
||||||
|
OptionRecommendation(name='preserve_cover_aspect_ratio',
|
||||||
|
recommended_value=False, help=_(
|
||||||
|
'When using an SVG cover, this option will cause the cover to scale '
|
||||||
|
'to cover the available screen area, but still preserve its aspect ratio '
|
||||||
|
'(ratio of width to height). That means there may be white borders '
|
||||||
|
'at the sides or top and bottom of the image, but the image will '
|
||||||
|
'never be distorted. Without this option the image may be slightly '
|
||||||
|
'distorted, but there will be no borders.'
|
||||||
|
)
|
||||||
|
),
|
||||||
|
|
||||||
])
|
])
|
||||||
|
|
||||||
recommendations = set([('pretty_print', True, OptionRecommendation.HIGH)])
|
recommendations = set([('pretty_print', True, OptionRecommendation.HIGH)])
|
||||||
|
|
||||||
NONSVG_TITLEPAGE_COVER = '''\
|
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
|
|
||||||
<head>
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
|
||||||
<meta name="calibre:cover" content="true" />
|
|
||||||
<title>Cover</title>
|
|
||||||
<style type="text/css" title="override_css">
|
|
||||||
@page {padding: 0pt; margin:0pt}
|
|
||||||
body { text-align: center; padding:0pt; margin: 0pt; }
|
|
||||||
div { padding:0pt; margin: 0pt; }
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div>
|
|
||||||
<img src="%s" alt="cover" style="height: 100%%" />
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
'''
|
|
||||||
|
|
||||||
TITLEPAGE_COVER = '''\
|
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
|
|
||||||
<head>
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
|
||||||
<meta name="calibre:cover" content="true" />
|
|
||||||
<title>Cover</title>
|
|
||||||
<style type="text/css" title="override_css">
|
|
||||||
@page {padding: 0pt; margin:0pt}
|
|
||||||
body { text-align: center; padding:0pt; margin: 0pt; }
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
|
||||||
width="100%%" height="100%%" viewBox="0 0 600 800"
|
|
||||||
preserveAspectRatio="xMidYMid meet">
|
|
||||||
<image width="600" height="800" xlink:href="%s"/>
|
|
||||||
</svg>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
'''
|
|
||||||
|
|
||||||
def workaround_webkit_quirks(self):
|
def workaround_webkit_quirks(self):
|
||||||
from calibre.ebooks.oeb.base import XPath
|
from calibre.ebooks.oeb.base import XPath
|
||||||
@ -259,97 +376,6 @@ class EPUBOutput(OutputFormatPlugin):
|
|||||||
ans += '\n</encryption>'
|
ans += '\n</encryption>'
|
||||||
return ans
|
return ans
|
||||||
|
|
||||||
def default_cover(self):
|
|
||||||
'''
|
|
||||||
Create a generic cover for books that dont have a cover
|
|
||||||
'''
|
|
||||||
from calibre.utils.pil_draw import draw_centered_text
|
|
||||||
from calibre.ebooks.metadata import authors_to_string
|
|
||||||
if self.opts.no_default_epub_cover:
|
|
||||||
return None
|
|
||||||
self.log('Generating default cover')
|
|
||||||
m = self.oeb.metadata
|
|
||||||
title = unicode(m.title[0])
|
|
||||||
authors = [unicode(x) for x in m.creator if x.role == 'aut']
|
|
||||||
|
|
||||||
import cStringIO
|
|
||||||
cover_file = cStringIO.StringIO()
|
|
||||||
try:
|
|
||||||
try:
|
|
||||||
from PIL import Image, ImageDraw, ImageFont
|
|
||||||
Image, ImageDraw, ImageFont
|
|
||||||
except ImportError:
|
|
||||||
import Image, ImageDraw, ImageFont
|
|
||||||
font_path = P('fonts/liberation/LiberationSerif-Bold.ttf')
|
|
||||||
app = '['+__appname__ +' '+__version__+']'
|
|
||||||
|
|
||||||
COVER_WIDTH, COVER_HEIGHT = 590, 750
|
|
||||||
img = Image.new('RGB', (COVER_WIDTH, COVER_HEIGHT), 'white')
|
|
||||||
draw = ImageDraw.Draw(img)
|
|
||||||
# Title
|
|
||||||
font = ImageFont.truetype(font_path, 44)
|
|
||||||
bottom = draw_centered_text(img, draw, font, title, 15, ysep=9)
|
|
||||||
# Authors
|
|
||||||
bottom += 14
|
|
||||||
font = ImageFont.truetype(font_path, 32)
|
|
||||||
authors = authors_to_string(authors)
|
|
||||||
bottom = draw_centered_text(img, draw, font, authors, bottom, ysep=7)
|
|
||||||
# Vanity
|
|
||||||
font = ImageFont.truetype(font_path, 28)
|
|
||||||
width, height = draw.textsize(app, font=font)
|
|
||||||
left = max(int((COVER_WIDTH - width)/2.), 0)
|
|
||||||
top = COVER_HEIGHT - height - 15
|
|
||||||
draw.text((left, top), app, fill=(0,0,0), font=font)
|
|
||||||
# Logo
|
|
||||||
logo = Image.open(I('library.png'), 'r')
|
|
||||||
width, height = logo.size
|
|
||||||
left = max(int((COVER_WIDTH - width)/2.), 0)
|
|
||||||
top = max(int((COVER_HEIGHT - height)/2.), 0)
|
|
||||||
img.paste(logo, (left, max(bottom, top)))
|
|
||||||
img = img.convert('RGB').convert('P', palette=Image.ADAPTIVE)
|
|
||||||
|
|
||||||
img.convert('RGB').save(cover_file, 'JPEG')
|
|
||||||
cover_file.flush()
|
|
||||||
id, href = self.oeb.manifest.generate('cover_image', 'cover_image.jpg')
|
|
||||||
item = self.oeb.manifest.add(id, href, guess_type('t.jpg')[0],
|
|
||||||
data=cover_file.getvalue())
|
|
||||||
m.clear('cover')
|
|
||||||
m.add('cover', item.id)
|
|
||||||
|
|
||||||
return item.href
|
|
||||||
except:
|
|
||||||
self.log.exception('Failed to generate default cover')
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def insert_cover(self):
|
|
||||||
from calibre.ebooks.oeb.base import urldefrag
|
|
||||||
from calibre import guess_type
|
|
||||||
g, m = self.oeb.guide, self.oeb.manifest
|
|
||||||
item = None
|
|
||||||
if 'titlepage' not in g:
|
|
||||||
if 'cover' in g:
|
|
||||||
href = g['cover'].href
|
|
||||||
else:
|
|
||||||
href = self.default_cover()
|
|
||||||
if href is not None:
|
|
||||||
templ = self.NONSVG_TITLEPAGE_COVER if self.opts.no_svg_cover \
|
|
||||||
else self.TITLEPAGE_COVER
|
|
||||||
tp = templ%unquote(href)
|
|
||||||
id, href = m.generate('titlepage', 'titlepage.xhtml')
|
|
||||||
item = m.add(id, href, guess_type('t.xhtml')[0],
|
|
||||||
data=etree.fromstring(tp))
|
|
||||||
else:
|
|
||||||
item = self.oeb.manifest.hrefs[
|
|
||||||
urldefrag(self.oeb.guide['titlepage'].href)[0]]
|
|
||||||
if item is not None:
|
|
||||||
self.oeb.spine.insert(0, item, True)
|
|
||||||
if 'cover' not in self.oeb.guide.refs:
|
|
||||||
self.oeb.guide.add('cover', 'Title Page', 'a')
|
|
||||||
self.oeb.guide.refs['cover'].href = item.href
|
|
||||||
if 'titlepage' in self.oeb.guide.refs:
|
|
||||||
self.oeb.guide.refs['titlepage'].href = item.href
|
|
||||||
|
|
||||||
def condense_ncx(self, ncx_path):
|
def condense_ncx(self, ncx_path):
|
||||||
if not self.opts.pretty_print:
|
if not self.opts.pretty_print:
|
||||||
tree = etree.parse(ncx_path)
|
tree = etree.parse(ncx_path)
|
||||||
|
@ -15,11 +15,39 @@ from calibre.customize.conversion import OutputFormatPlugin, \
|
|||||||
OptionRecommendation
|
OptionRecommendation
|
||||||
from calibre.ebooks.metadata.opf2 import OPF
|
from calibre.ebooks.metadata.opf2 import OPF
|
||||||
from calibre.ptempfile import TemporaryDirectory
|
from calibre.ptempfile import TemporaryDirectory
|
||||||
from calibre.ebooks.pdf.writer import PDFWriter, ImagePDFWriter, PDFMetadata
|
from calibre.ebooks.pdf.writer import PDFWriter, ImagePDFWriter, PDFMetadata, \
|
||||||
|
get_pdf_page_size
|
||||||
from calibre.ebooks.pdf.pageoptions import UNITS, PAPER_SIZES, \
|
from calibre.ebooks.pdf.pageoptions import UNITS, PAPER_SIZES, \
|
||||||
ORIENTATIONS
|
ORIENTATIONS
|
||||||
|
from calibre.ebooks.epub.output import CoverManager
|
||||||
|
|
||||||
class PDFOutput(OutputFormatPlugin):
|
class CoverManagerPDF(CoverManager):
|
||||||
|
|
||||||
|
def setup_cover(self, opts):
|
||||||
|
width, height = get_pdf_page_size(opts)
|
||||||
|
factor = opts.output_profile.dpi
|
||||||
|
self.NONSVG_TITLEPAGE_COVER = '''\
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||||
|
<meta name="calibre:cover" content="true" />
|
||||||
|
<title>Cover</title>
|
||||||
|
<style type="text/css" title="override_css">
|
||||||
|
@page {padding: 0pt; margin:0pt}
|
||||||
|
body { text-align: center; padding:0pt; margin: 0pt; }
|
||||||
|
div { padding:0pt; margin: 0pt; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div>
|
||||||
|
<img src="%%s" alt="cover" width="%d" height="%d" />
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
'''%(int(width*factor), int(height*factor)-5)
|
||||||
|
|
||||||
|
|
||||||
|
class PDFOutput(OutputFormatPlugin, CoverManagerPDF):
|
||||||
|
|
||||||
name = 'PDF Output'
|
name = 'PDF Output'
|
||||||
author = 'John Schember'
|
author = 'John Schember'
|
||||||
@ -47,6 +75,7 @@ class PDFOutput(OutputFormatPlugin):
|
|||||||
])
|
])
|
||||||
|
|
||||||
def convert(self, oeb_book, output_path, input_plugin, opts, log):
|
def convert(self, oeb_book, output_path, input_plugin, opts, log):
|
||||||
|
self.oeb = oeb_book
|
||||||
self.input_plugin, self.opts, self.log = input_plugin, opts, log
|
self.input_plugin, self.opts, self.log = input_plugin, opts, log
|
||||||
self.output_path = output_path
|
self.output_path = output_path
|
||||||
self.metadata = oeb_book.metadata
|
self.metadata = oeb_book.metadata
|
||||||
@ -63,6 +92,10 @@ class PDFOutput(OutputFormatPlugin):
|
|||||||
|
|
||||||
def convert_text(self, oeb_book):
|
def convert_text(self, oeb_book):
|
||||||
self.log.debug('Serializing oeb input to disk for processing...')
|
self.log.debug('Serializing oeb input to disk for processing...')
|
||||||
|
self.opts.no_svg_cover = True
|
||||||
|
self.opts.no_default_epub_cover = True
|
||||||
|
self.setup_cover(self.opts)
|
||||||
|
self.insert_cover()
|
||||||
with TemporaryDirectory('_pdf_out') as oeb_dir:
|
with TemporaryDirectory('_pdf_out') as oeb_dir:
|
||||||
from calibre.customize.ui import plugin_for_output_format
|
from calibre.customize.ui import plugin_for_output_format
|
||||||
oeb_output = plugin_for_output_format('oeb')
|
oeb_output = plugin_for_output_format('oeb')
|
||||||
|
@ -18,11 +18,70 @@ from calibre.ebooks.metadata import authors_to_string
|
|||||||
|
|
||||||
from PyQt4 import QtCore
|
from PyQt4 import QtCore
|
||||||
from PyQt4.Qt import QUrl, QEventLoop, SIGNAL, QObject, \
|
from PyQt4.Qt import QUrl, QEventLoop, SIGNAL, QObject, \
|
||||||
QPrinter, QMetaObject, QSizeF, Qt
|
QPrinter, QMetaObject, QSizeF, Qt, QPainter
|
||||||
from PyQt4.QtWebKit import QWebView
|
from PyQt4.QtWebKit import QWebView
|
||||||
|
|
||||||
from pyPdf import PdfFileWriter, PdfFileReader
|
from pyPdf import PdfFileWriter, PdfFileReader
|
||||||
|
|
||||||
|
def get_custom_size(opts):
|
||||||
|
custom_size = None
|
||||||
|
if opts.custom_size != None:
|
||||||
|
width, sep, height = opts.custom_size.partition('x')
|
||||||
|
if height != '':
|
||||||
|
try:
|
||||||
|
width = int(width)
|
||||||
|
height = int(height)
|
||||||
|
custom_size = (width, height)
|
||||||
|
except:
|
||||||
|
custom_size = None
|
||||||
|
return custom_size
|
||||||
|
|
||||||
|
def get_pdf_page_size(opts):
|
||||||
|
from calibre.gui2 import is_ok_to_use_qt
|
||||||
|
if not is_ok_to_use_qt():
|
||||||
|
raise Exception('Not OK to use Qt')
|
||||||
|
|
||||||
|
printer = QPrinter(QPrinter.HighResolution)
|
||||||
|
custom_size = get_custom_size(opts)
|
||||||
|
|
||||||
|
if opts.output_profile.short_name == 'default':
|
||||||
|
if custom_size is None:
|
||||||
|
printer.setPaperSize(paper_size(opts.paper_size))
|
||||||
|
else:
|
||||||
|
printer.setPaperSize(QSizeF(custom_size[0], custom_size[1]), unit(opts.unit))
|
||||||
|
else:
|
||||||
|
printer.setPaperSize(QSizeF(opts.output_profile.width / opts.output_profile.dpi,
|
||||||
|
opts.output_profile.height / opts.output_profile.dpi), QPrinter.Inch)
|
||||||
|
|
||||||
|
printer.setPageMargins(0, 0, 0, 0, QPrinter.Point)
|
||||||
|
printer.setOrientation(orientation(opts.orientation))
|
||||||
|
printer.setOutputFormat(QPrinter.PdfFormat)
|
||||||
|
|
||||||
|
size = printer.paperSize(QPrinter.Millimeter)
|
||||||
|
|
||||||
|
return size.width() / 10, size.height() / 10
|
||||||
|
|
||||||
|
def get_imagepdf_page_size(opts):
|
||||||
|
printer = QPrinter(QPrinter.HighResolution)
|
||||||
|
custom_size = get_custom_size(opts)
|
||||||
|
|
||||||
|
if opts.output_profile.short_name == 'default':
|
||||||
|
if custom_size == None:
|
||||||
|
printer.setPaperSize(paper_size(opts.paper_size))
|
||||||
|
else:
|
||||||
|
printer.setPaperSize(QSizeF(custom_size[0], custom_size[1]), unit(opts.unit))
|
||||||
|
else:
|
||||||
|
printer.setPaperSize(QSizeF(opts.output_profile.comic_screen_size[0] / opts.output_profile.dpi,
|
||||||
|
opts.output_profile.comic_screen_size[1] / opts.output_profile.dpi), QPrinter.Inch)
|
||||||
|
|
||||||
|
printer.setPageMargins(0, 0, 0, 0, QPrinter.Point)
|
||||||
|
printer.setOrientation(orientation(opts.orientation))
|
||||||
|
printer.setOutputFormat(QPrinter.PdfFormat)
|
||||||
|
|
||||||
|
size = printer.paperSize(QPrinter.Millimeter)
|
||||||
|
|
||||||
|
return size.width() / 10, size.height() / 10
|
||||||
|
|
||||||
class PDFMetadata(object):
|
class PDFMetadata(object):
|
||||||
def __init__(self, oeb_metadata=None):
|
def __init__(self, oeb_metadata=None):
|
||||||
self.title = _('Unknown')
|
self.title = _('Unknown')
|
||||||
@ -36,6 +95,7 @@ class PDFMetadata(object):
|
|||||||
|
|
||||||
|
|
||||||
class PDFWriter(QObject):
|
class PDFWriter(QObject):
|
||||||
|
|
||||||
def __init__(self, opts, log):
|
def __init__(self, opts, log):
|
||||||
from calibre.gui2 import is_ok_to_use_qt
|
from calibre.gui2 import is_ok_to_use_qt
|
||||||
if not is_ok_to_use_qt():
|
if not is_ok_to_use_qt():
|
||||||
@ -46,25 +106,15 @@ class PDFWriter(QObject):
|
|||||||
|
|
||||||
self.loop = QEventLoop()
|
self.loop = QEventLoop()
|
||||||
self.view = QWebView()
|
self.view = QWebView()
|
||||||
|
self.view.setRenderHints(QPainter.Antialiasing|QPainter.TextAntialiasing|QPainter.SmoothPixmapTransform)
|
||||||
self.connect(self.view, SIGNAL('loadFinished(bool)'), self._render_html)
|
self.connect(self.view, SIGNAL('loadFinished(bool)'), self._render_html)
|
||||||
self.render_queue = []
|
self.render_queue = []
|
||||||
self.combine_queue = []
|
self.combine_queue = []
|
||||||
self.tmp_path = PersistentTemporaryDirectory('_pdf_output_parts')
|
self.tmp_path = PersistentTemporaryDirectory('_pdf_output_parts')
|
||||||
|
|
||||||
self.custom_size = None
|
|
||||||
if opts.custom_size != None:
|
|
||||||
width, sep, height = opts.custom_size.partition('x')
|
|
||||||
if height != '':
|
|
||||||
try:
|
|
||||||
width = int(width)
|
|
||||||
height = int(height)
|
|
||||||
self.custom_size = (width, height)
|
|
||||||
except:
|
|
||||||
self.custom_size = None
|
|
||||||
|
|
||||||
self.opts = opts
|
self.opts = opts
|
||||||
|
|
||||||
self.size = self._size()
|
self.size = get_pdf_page_size(opts)
|
||||||
|
|
||||||
def dump(self, items, out_stream, pdf_metadata):
|
def dump(self, items, out_stream, pdf_metadata):
|
||||||
self.metadata = pdf_metadata
|
self.metadata = pdf_metadata
|
||||||
@ -77,27 +127,6 @@ class PDFWriter(QObject):
|
|||||||
QMetaObject.invokeMethod(self, "_render_book", Qt.QueuedConnection)
|
QMetaObject.invokeMethod(self, "_render_book", Qt.QueuedConnection)
|
||||||
self.loop.exec_()
|
self.loop.exec_()
|
||||||
|
|
||||||
def _size(self):
|
|
||||||
'''
|
|
||||||
The size of a pdf page in cm.
|
|
||||||
'''
|
|
||||||
printer = QPrinter(QPrinter.HighResolution)
|
|
||||||
|
|
||||||
if self.opts.output_profile.short_name == 'default':
|
|
||||||
if self.custom_size == None:
|
|
||||||
printer.setPaperSize(paper_size(self.opts.paper_size))
|
|
||||||
else:
|
|
||||||
printer.setPaperSize(QSizeF(self.custom_size[0], self.custom_size[1]), unit(self.opts.unit))
|
|
||||||
else:
|
|
||||||
printer.setPaperSize(QSizeF(self.opts.output_profile.width / self.opts.output_profile.dpi, self.opts.output_profile.height / self.opts.output_profile.dpi), QPrinter.Inch)
|
|
||||||
|
|
||||||
printer.setPageMargins(0, 0, 0, 0, QPrinter.Point)
|
|
||||||
printer.setOrientation(orientation(self.opts.orientation))
|
|
||||||
printer.setOutputFormat(QPrinter.PdfFormat)
|
|
||||||
|
|
||||||
size = printer.paperSize(QPrinter.Millimeter)
|
|
||||||
|
|
||||||
return size.width() / 10, size.height() / 10
|
|
||||||
|
|
||||||
@QtCore.pyqtSignature('_render_book()')
|
@QtCore.pyqtSignature('_render_book()')
|
||||||
def _render_book(self):
|
def _render_book(self):
|
||||||
@ -151,6 +180,10 @@ class PDFWriter(QObject):
|
|||||||
|
|
||||||
class ImagePDFWriter(PDFWriter):
|
class ImagePDFWriter(PDFWriter):
|
||||||
|
|
||||||
|
def __init__(self, opts, log):
|
||||||
|
PDFWriter.__init__(self, opts, log)
|
||||||
|
self.size = get_imagepdf_page_size(opts)
|
||||||
|
|
||||||
def _render_next(self):
|
def _render_next(self):
|
||||||
item = str(self.render_queue.pop(0))
|
item = str(self.render_queue.pop(0))
|
||||||
self.combine_queue.append(os.path.join(self.tmp_path, '%i.pdf' % (len(self.combine_queue) + 1)))
|
self.combine_queue.append(os.path.join(self.tmp_path, '%i.pdf' % (len(self.combine_queue) + 1)))
|
||||||
@ -163,22 +196,4 @@ class ImagePDFWriter(PDFWriter):
|
|||||||
|
|
||||||
self.view.setHtml(html)
|
self.view.setHtml(html)
|
||||||
|
|
||||||
def _size(self):
|
|
||||||
printer = QPrinter(QPrinter.HighResolution)
|
|
||||||
|
|
||||||
if self.opts.output_profile.short_name == 'default':
|
|
||||||
if self.custom_size == None:
|
|
||||||
printer.setPaperSize(paper_size(self.opts.paper_size))
|
|
||||||
else:
|
|
||||||
printer.setPaperSize(QSizeF(self.custom_size[0], self.custom_size[1]), unit(self.opts.unit))
|
|
||||||
else:
|
|
||||||
printer.setPaperSize(QSizeF(self.opts.output_profile.comic_screen_size[0] / self.opts.output_profile.dpi, self.opts.output_profile.comic_screen_size[1] / self.opts.output_profile.dpi), QPrinter.Inch)
|
|
||||||
|
|
||||||
printer.setPageMargins(0, 0, 0, 0, QPrinter.Point)
|
|
||||||
printer.setOrientation(orientation(self.opts.orientation))
|
|
||||||
printer.setOutputFormat(QPrinter.PdfFormat)
|
|
||||||
|
|
||||||
size = printer.paperSize(QPrinter.Millimeter)
|
|
||||||
|
|
||||||
return size.width() / 10, size.height() / 10
|
|
||||||
|
|
||||||
|
@ -18,8 +18,11 @@ 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, 'epub_output',
|
Widget.__init__(self, parent, 'epub_output',
|
||||||
['dont_split_on_page_breaks', 'flow_size',
|
['dont_split_on_page_breaks', 'flow_size',
|
||||||
'no_default_epub_cover', 'no_svg_cover']
|
'no_default_epub_cover', 'no_svg_cover',
|
||||||
|
'preserve_cover_aspect_ratio',]
|
||||||
)
|
)
|
||||||
|
for i in range(2):
|
||||||
|
self.opt_no_svg_cover.toggle()
|
||||||
self.db, self.book_id = db, book_id
|
self.db, self.book_id = db, book_id
|
||||||
self.initialize_options(get_option, get_help, db, book_id)
|
self.initialize_options(get_option, get_help, db, book_id)
|
||||||
|
|
||||||
|
@ -14,13 +14,34 @@
|
|||||||
<string>Form</string>
|
<string>Form</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QGridLayout" name="gridLayout">
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
<item row="0" column="0" colspan="2">
|
<item row="0" column="0">
|
||||||
<widget class="QCheckBox" name="opt_dont_split_on_page_breaks">
|
<widget class="QCheckBox" name="opt_dont_split_on_page_breaks">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Do not &split on page breaks</string>
|
<string>Do not &split on page breaks</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QCheckBox" name="opt_no_default_epub_cover">
|
||||||
|
<property name="text">
|
||||||
|
<string>No default &cover</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QCheckBox" name="opt_no_svg_cover">
|
||||||
|
<property name="text">
|
||||||
|
<string>No &SVG cover</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QCheckBox" name="opt_preserve_cover_aspect_ratio">
|
||||||
|
<property name="text">
|
||||||
|
<string>Preserve cover &aspect ratio</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item row="3" column="0">
|
<item row="3" column="0">
|
||||||
<widget class="QLabel" name="label">
|
<widget class="QLabel" name="label">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
@ -60,22 +81,25 @@
|
|||||||
</property>
|
</property>
|
||||||
</spacer>
|
</spacer>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="0">
|
|
||||||
<widget class="QCheckBox" name="opt_no_default_epub_cover">
|
|
||||||
<property name="text">
|
|
||||||
<string>No default &cover</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="0">
|
|
||||||
<widget class="QCheckBox" name="opt_no_svg_cover">
|
|
||||||
<property name="text">
|
|
||||||
<string>No &SVG cover</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<resources/>
|
<resources/>
|
||||||
<connections/>
|
<connections>
|
||||||
|
<connection>
|
||||||
|
<sender>opt_no_svg_cover</sender>
|
||||||
|
<signal>toggled(bool)</signal>
|
||||||
|
<receiver>opt_preserve_cover_aspect_ratio</receiver>
|
||||||
|
<slot>setDisabled(bool)</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>81</x>
|
||||||
|
<y>73</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>237</x>
|
||||||
|
<y>68</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
</connections>
|
||||||
</ui>
|
</ui>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user