mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-07 18:24:30 -04:00
Basic PDF output works
This commit is contained in:
parent
4abfcc46ab
commit
b25766f6aa
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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:
|
||||
|
Loading…
x
Reference in New Issue
Block a user