Basic PDF output works

This commit is contained in:
Kovid Goyal 2019-07-08 16:47:25 +05:30
parent 4abfcc46ab
commit b25766f6aa
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
4 changed files with 91 additions and 26 deletions

View File

@ -261,4 +261,4 @@ class PDFOutput(OutputFormatPlugin):
oeb_output = plugin_for_output_format('oeb')
oeb_output.convert(oeb_book, oeb_dir, self.input_plugin, self.opts, self.log)
opfpath = glob.glob(os.path.join(oeb_dir, '*.opf'))[0]
convert(opfpath, self.log, self.opts)
convert(opfpath, self.opts, self.output_path, self.log)

View File

@ -357,7 +357,7 @@ def remove_name_attributes(root):
elem.set('id', elem.attrib.pop('name'))
def merge_html(container, names, master):
def merge_html(container, names, master, insert_page_breaks=False):
p = container.parsed
root = p(master)
@ -428,6 +428,9 @@ def merge_html(container, names, master):
if q in amap:
a.set('href', '#' + amap[q])
if insert_page_breaks:
master_body.append(master_body.makeelement(XHTML('div'), style='page-break-after:always'))
for child in children:
if isinstance(child, string_or_bytes):
add_text(master_body, child)

View File

@ -6,11 +6,20 @@ from __future__ import absolute_import, division, print_function, unicode_litera
import os
from PyQt5.Qt import QApplication, QUrl, QTimer
from PyQt5.QtWebEngineWidgets import QWebEnginePage
from calibre.constants import iswindows
from calibre.ebooks.oeb.polish.container import Container as ContainerBase
from calibre.ebooks.oeb.polish.split import merge_html
from calibre.gui2 import setup_unix_signals
from calibre.gui2.webengine import secure_webengine
from calibre.ebooks.pdf.image_writer import get_page_layout
from calibre.utils.logging import default_log
from polyglot.builtins import range
OK, LOAD_FAILED, KILL_SIGNAL = range(0, 3)
class Container(ContainerBase):
@ -27,15 +36,18 @@ class Renderer(QWebEnginePage):
def __init__(self, opts):
QWebEnginePage.__init__(self)
secure_webengine(self)
self.settle_time = 0
s = self.settings()
s.setAttribute(s.JavascriptEnabled, True)
s.setFontSize(s.DefaultFontSize, opts.pdf_default_font_size)
s.setFontSize(s.DefaultFixedFontSize, opts.pdf_mono_font_size)
s.setFontSize(s.MinimumLogicalFontSize, 8)
s.setFontSize(s.MinimumFontSize, 8)
std = {'serif':opts.pdf_serif_family, 'sans':opts.pdf_sans_family,
'mono':opts.pdf_mono_family}.get(opts.pdf_standard_font,
opts.pdf_serif_family)
std = {
'serif': opts.pdf_serif_family,
'sans' : opts.pdf_sans_family,
'mono' : opts.pdf_mono_family
}.get(opts.pdf_standard_font, opts.pdf_serif_family)
if std:
s.setFontFamily(s.StandardFont, std)
if opts.pdf_serif_family:
@ -45,14 +57,60 @@ class Renderer(QWebEnginePage):
if opts.pdf_mono_family:
s.setFontFamily(s.FixedFont, opts.pdf_mono_family)
self.loadFinished.connect(self.load_finished)
if not iswindows:
setup_unix_signals(self)
def convert(opf_path, log, opts):
def load_finished(self, ok):
if not ok:
QApplication.instance().exit(LOAD_FAILED)
return
QTimer.singleShot(int(1000 * self.settle_time), self.print_to_pdf)
def signal_received(self, read_fd):
try:
os.read(read_fd, 1024)
except EnvironmentError:
return
QApplication.instance().exit(KILL_SIGNAL)
def print_to_pdf(self):
self.printToPdf(self.printing_done, self.page_layout)
def printing_done(self, pdf_data):
self.pdf_data = pdf_data
QApplication.instance().exit(OK)
def convert_html_file(self, path, page_layout, settle_time=0):
self.settle_time = settle_time
self.page_layout = page_layout
self.pdf_data = None
self.setUrl(QUrl.fromLocalFile(path))
ret = QApplication.exec_()
if ret == LOAD_FAILED:
raise SystemExit('Failed to load {}'.format(path))
if ret == KILL_SIGNAL:
raise SystemExit('Kill signal received')
if ret != OK:
raise SystemExit('Unknown error occurred')
return self.pdf_data
def convert(opf_path, opts, output_path=None, log=default_log):
container = Container(opf_path, log)
spine_names = [name for name in container.spine_names]
spine_names = [name for name, is_linear in container.spine_names]
master = spine_names[0]
if len(spine_names) > 1:
merge_html(container, spine_names, master)
merge_html(container, spine_names, master, insert_page_breaks=True)
container.commit()
index_file = container.name_to_abspath(master)
index_file
renderer = Renderer(opts)
page_layout = get_page_layout(opts)
pdf_data = renderer.convert_html_file(index_file, page_layout, settle_time=1)
if output_path is None:
return pdf_data
with open(output_path, 'wb') as f:
f.write(pdf_data)

View File

@ -813,6 +813,26 @@ def setup_hidpi():
prints('Not controlling automatic hidpi scaling')
def setup_unix_signals(self):
if hasattr(os, 'pipe2'):
read_fd, write_fd = os.pipe2(os.O_CLOEXEC | os.O_NONBLOCK)
else:
import fcntl
read_fd, write_fd = os.pipe()
cloexec_flag = getattr(fcntl, 'FD_CLOEXEC', 1)
for fd in (read_fd, write_fd):
flags = fcntl.fcntl(fd, fcntl.F_GETFD)
fcntl.fcntl(fd, fcntl.F_SETFD, flags | cloexec_flag | os.O_NONBLOCK)
for sig in (signal.SIGINT, signal.SIGTERM):
signal.signal(sig, lambda x, y: None)
signal.siginterrupt(sig, False)
signal.set_wakeup_fd(write_fd)
self.signal_notifier = QSocketNotifier(read_fd, QSocketNotifier.Read, self)
self.signal_notifier.setEnabled(True)
self.signal_notifier.activated.connect(self.signal_received, type=Qt.QueuedConnection)
class Application(QApplication):
shutdown_signal_received = pyqtSignal()
@ -1066,23 +1086,7 @@ class Application(QApplication):
self.setQuitOnLastWindowClosed(True)
def setup_unix_signals(self):
import fcntl
if hasattr(os, 'pipe2'):
read_fd, write_fd = os.pipe2(os.O_CLOEXEC | os.O_NONBLOCK)
else:
read_fd, write_fd = os.pipe()
cloexec_flag = getattr(fcntl, 'FD_CLOEXEC', 1)
for fd in (read_fd, write_fd):
flags = fcntl.fcntl(fd, fcntl.F_GETFD)
fcntl.fcntl(fd, fcntl.F_SETFD, flags | cloexec_flag | os.O_NONBLOCK)
for sig in (signal.SIGINT, signal.SIGTERM):
signal.signal(sig, lambda x, y: None)
signal.siginterrupt(sig, False)
signal.set_wakeup_fd(write_fd)
self.signal_notifier = QSocketNotifier(read_fd, QSocketNotifier.Read, self)
self.signal_notifier.setEnabled(True)
self.signal_notifier.activated.connect(self.signal_received, type=Qt.QueuedConnection)
setup_unix_signals(self)
def signal_received(self, read_fd):
try: