mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Beginning of PDF conversion
This commit is contained in:
parent
358ec20ceb
commit
9ff64bd715
69
src/calibre/ebooks/pdf/from_any.py
Normal file
69
src/calibre/ebooks/pdf/from_any.py
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
'''
|
||||||
|
Convert any ebook format to PDF.
|
||||||
|
'''
|
||||||
|
|
||||||
|
from __future__ import with_statement
|
||||||
|
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net ' \
|
||||||
|
'and Marshall T. Vandegrift <llasram@gmail.com>' \
|
||||||
|
'and John Schember <john@nachtimwald.com>'
|
||||||
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
|
import sys, os, glob, logging
|
||||||
|
|
||||||
|
from calibre.ebooks.epub.from_any import any2epub, formats, USAGE
|
||||||
|
from calibre.ebooks.epub import config as common_config
|
||||||
|
from calibre.ptempfile import TemporaryDirectory
|
||||||
|
from calibre.ebooks.pdf.writer import oeb2pdf, config as pdf_config
|
||||||
|
|
||||||
|
def config(defaults=None):
|
||||||
|
c = common_config(defaults=defaults, name='pdf')
|
||||||
|
c.remove_opt('profile')
|
||||||
|
pdfc = pdf_config(defaults=defaults)
|
||||||
|
c.update(pdfc)
|
||||||
|
return c
|
||||||
|
|
||||||
|
def option_parser(usage=USAGE):
|
||||||
|
usage = usage % ('PDF', formats())
|
||||||
|
parser = config().option_parser(usage=usage)
|
||||||
|
return parser
|
||||||
|
|
||||||
|
def any2pdf(opts, path, notification=None):
|
||||||
|
ext = os.path.splitext(path)[1]
|
||||||
|
if not ext:
|
||||||
|
raise ValueError('Unknown file type: '+path)
|
||||||
|
ext = ext.lower()[1:]
|
||||||
|
|
||||||
|
if opts.output is None:
|
||||||
|
opts.output = os.path.splitext(os.path.basename(path))[0]+'.pdf'
|
||||||
|
|
||||||
|
opts.output = os.path.abspath(opts.output)
|
||||||
|
orig_output = opts.output
|
||||||
|
|
||||||
|
with TemporaryDirectory('_any2pdf') as tdir:
|
||||||
|
oebdir = os.path.join(tdir, 'oeb')
|
||||||
|
os.mkdir(oebdir)
|
||||||
|
opts.output = os.path.join(tdir, 'dummy.epub')
|
||||||
|
opts.profile = 'None'
|
||||||
|
opts.dont_split_on_page_breaks = True
|
||||||
|
orig_bfs = opts.base_font_size2
|
||||||
|
opts.base_font_size2 = 0
|
||||||
|
any2epub(opts, path, create_epub=False, oeb_cover=True, extract_to=oebdir)
|
||||||
|
opts.base_font_size2 = orig_bfs
|
||||||
|
opf = glob.glob(os.path.join(oebdir, '*.opf'))[0]
|
||||||
|
opts.output = orig_output
|
||||||
|
logging.getLogger('html2epub').info(_('Creating PDF file from EPUB...'))
|
||||||
|
oeb2pdf(opts, opf)
|
||||||
|
|
||||||
|
def main(args=sys.argv):
|
||||||
|
parser = option_parser()
|
||||||
|
opts, args = parser.parse_args(args)
|
||||||
|
if len(args) < 2:
|
||||||
|
parser.print_help()
|
||||||
|
print 'No input file specified.'
|
||||||
|
return 1
|
||||||
|
any2pdf(opts, args[1])
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.exit(main())
|
153
src/calibre/ebooks/pdf/writer.py
Normal file
153
src/calibre/ebooks/pdf/writer.py
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
'''
|
||||||
|
Write content to PDF.
|
||||||
|
'''
|
||||||
|
from __future__ import with_statement
|
||||||
|
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2009, John Schember <john@nachtimwald.com>'
|
||||||
|
|
||||||
|
import os, logging, shutil, sys
|
||||||
|
from calibre.ebooks.oeb.base import Logger, OEBBook
|
||||||
|
from calibre.ebooks.oeb.profile import Context
|
||||||
|
from calibre.ebooks.epub.iterator import SpineItem
|
||||||
|
from calibre.ebooks.metadata.opf2 import OPF
|
||||||
|
from calibre.ptempfile import PersistentTemporaryDirectory
|
||||||
|
from calibre.customize.ui import run_plugins_on_postprocess
|
||||||
|
from calibre.utils.config import Config, StringConfig
|
||||||
|
|
||||||
|
from PyQt4 import QtCore
|
||||||
|
from PyQt4.Qt import QUrl, QEventLoop, SIGNAL, QObject, QApplication, QPrinter, \
|
||||||
|
QMetaObject
|
||||||
|
from PyQt4.Qt import *
|
||||||
|
from PyQt4.QtWebKit import QWebView
|
||||||
|
|
||||||
|
from pyPdf import PdfFileWriter, PdfFileReader
|
||||||
|
|
||||||
|
class PDFWriter(QObject):
|
||||||
|
def __init__(self):
|
||||||
|
if QApplication.instance() is None:
|
||||||
|
QApplication([])
|
||||||
|
QObject.__init__(self)
|
||||||
|
|
||||||
|
self.loop = QEventLoop()
|
||||||
|
self.view = QWebView()
|
||||||
|
self.connect(self.view, SIGNAL('loadFinished(bool)'), self._render_html)
|
||||||
|
self.render_queue = []
|
||||||
|
self.combine_queue = []
|
||||||
|
self.tmp_path = PersistentTemporaryDirectory('_any2pdf_parts')
|
||||||
|
|
||||||
|
def dump(self, oeb, oebpath, path):
|
||||||
|
self._reset()
|
||||||
|
|
||||||
|
opf = OPF(oebpath, os.path.dirname(oebpath))
|
||||||
|
self.render_queue = [SpineItem(i.path) for i in opf.spine]
|
||||||
|
|
||||||
|
self.path = path
|
||||||
|
|
||||||
|
QMetaObject.invokeMethod(self, "_render_book", Qt.QueuedConnection)
|
||||||
|
self.loop.exec_()
|
||||||
|
|
||||||
|
@QtCore.pyqtSignature('_render_book()')
|
||||||
|
def _render_book(self):
|
||||||
|
if len(self.render_queue) == 0:
|
||||||
|
self._write()
|
||||||
|
else:
|
||||||
|
self._render_next()
|
||||||
|
|
||||||
|
def _render_next(self):
|
||||||
|
item = str(self.render_queue.pop(0))
|
||||||
|
self.combine_queue.append(os.path.join(self.tmp_path, '%s.pdf' % os.path.basename(item)))
|
||||||
|
|
||||||
|
self.view.load(QUrl(item))
|
||||||
|
|
||||||
|
def _render_html(self, ok):
|
||||||
|
if ok:
|
||||||
|
printer = QPrinter(QPrinter.HighResolution)
|
||||||
|
printer.setPageMargins(1, 1, 1, 1, QPrinter.Inch)
|
||||||
|
printer.setOutputFormat(QPrinter.PdfFormat)
|
||||||
|
printer.setOutputFileName(os.path.join(self.tmp_path, '%s.pdf' % os.path.basename(str(self.view.url().toLocalFile()))))
|
||||||
|
self.view.print_(printer)
|
||||||
|
self._render_book()
|
||||||
|
|
||||||
|
def _reset(self):
|
||||||
|
self.render_queue = []
|
||||||
|
self.combine_queue = []
|
||||||
|
self.path = ''
|
||||||
|
if os.path.exists(self.tmp_path):
|
||||||
|
shutil.rmtree(self.tmp_path, True)
|
||||||
|
self.tmp_path = PersistentTemporaryDirectory('_any2pdf_parts')
|
||||||
|
|
||||||
|
def _write(self):
|
||||||
|
print self.path
|
||||||
|
try:
|
||||||
|
outPDF = PdfFileWriter()
|
||||||
|
for item in self.combine_queue:
|
||||||
|
inputPDF = PdfFileReader(file(item, 'rb'))
|
||||||
|
for page in inputPDF.pages:
|
||||||
|
outPDF.addPage(page)
|
||||||
|
outputStream = file(self.path, 'wb')
|
||||||
|
outPDF.write(outputStream)
|
||||||
|
outputStream.close()
|
||||||
|
finally:
|
||||||
|
self._reset()
|
||||||
|
self.loop.exit(0)
|
||||||
|
|
||||||
|
|
||||||
|
def config(defaults=None):
|
||||||
|
desc = _('Options to control the conversion to PDF')
|
||||||
|
if defaults is None:
|
||||||
|
c = Config('pdf', desc)
|
||||||
|
else:
|
||||||
|
c = StringConfig(defaults, desc)
|
||||||
|
|
||||||
|
pdf = c.add_group('PDF', _('PDF options.'))
|
||||||
|
|
||||||
|
return c
|
||||||
|
|
||||||
|
|
||||||
|
def option_parser():
|
||||||
|
c = config()
|
||||||
|
parser = c.option_parser(usage='%prog '+_('[options]')+' file.opf')
|
||||||
|
parser.add_option(
|
||||||
|
'-o', '--output', default=None,
|
||||||
|
help=_('Output file. Default is derived from input filename.'))
|
||||||
|
parser.add_option(
|
||||||
|
'-v', '--verbose', default=0, action='count',
|
||||||
|
help=_('Useful for debugging.'))
|
||||||
|
return parser
|
||||||
|
|
||||||
|
def oeb2pdf(opts, inpath):
|
||||||
|
logger = Logger(logging.getLogger('oeb2pdf'))
|
||||||
|
logger.setup_cli_handler(opts.verbose)
|
||||||
|
outpath = opts.output
|
||||||
|
if outpath is None:
|
||||||
|
outpath = os.path.basename(inpath)
|
||||||
|
outpath = os.path.splitext(outpath)[0] + '.pdf'
|
||||||
|
# source = opts.source_profile
|
||||||
|
# if source not in Context.PROFILES:
|
||||||
|
# logger.error(_('Unknown source profile %r') % source)
|
||||||
|
# return 1
|
||||||
|
# dest = opts.dest_profile
|
||||||
|
# if dest not in Context.PROFILES:
|
||||||
|
# logger.error(_('Unknown destination profile %r') % dest)
|
||||||
|
# return 1
|
||||||
|
|
||||||
|
oeb = OEBBook(inpath, logger=logger, encoding=opts.encoding)
|
||||||
|
writer = PDFWriter()
|
||||||
|
writer.dump(oeb, inpath, outpath)
|
||||||
|
run_plugins_on_postprocess(outpath, 'pdf')
|
||||||
|
logger.info(_('Output written to ') + outpath)
|
||||||
|
|
||||||
|
def main(argv=sys.argv):
|
||||||
|
parser = option_parser()
|
||||||
|
opts, args = parser.parse_args(argv[1:])
|
||||||
|
if len(args) != 1:
|
||||||
|
parser.print_help()
|
||||||
|
return 1
|
||||||
|
inpath = args[0]
|
||||||
|
retval = oeb2pdf(opts, inpath)
|
||||||
|
return retval
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.exit(main())
|
||||||
|
|
@ -51,6 +51,7 @@ entry_points = {
|
|||||||
'any2epub = calibre.ebooks.epub.from_any:main',
|
'any2epub = calibre.ebooks.epub.from_any:main',
|
||||||
'any2lit = calibre.ebooks.lit.from_any:main',
|
'any2lit = calibre.ebooks.lit.from_any:main',
|
||||||
'any2mobi = calibre.ebooks.mobi.from_any:main',
|
'any2mobi = calibre.ebooks.mobi.from_any:main',
|
||||||
|
'any2pdf = calibre.ebooks.pdf.from_any:main',
|
||||||
'lrf2lrs = calibre.ebooks.lrf.lrfparser:main',
|
'lrf2lrs = calibre.ebooks.lrf.lrfparser:main',
|
||||||
'lrs2lrf = calibre.ebooks.lrf.lrs.convert_from:main',
|
'lrs2lrf = calibre.ebooks.lrf.lrs.convert_from:main',
|
||||||
'pdfreflow = calibre.ebooks.lrf.pdf.reflow:main',
|
'pdfreflow = calibre.ebooks.lrf.pdf.reflow:main',
|
||||||
|
@ -71,6 +71,9 @@ PARALLEL_FUNCS = {
|
|||||||
'any2mobi' :
|
'any2mobi' :
|
||||||
('calibre.ebooks.mobi.from_any', 'any2mobi', {}, None),
|
('calibre.ebooks.mobi.from_any', 'any2mobi', {}, None),
|
||||||
|
|
||||||
|
'any2pdf' :
|
||||||
|
('calibre.ebooks.pdf.from_any', 'any2pdf', {}, None),
|
||||||
|
|
||||||
'feeds2mobi' :
|
'feeds2mobi' :
|
||||||
('calibre.ebooks.mobi.from_feeds', 'main', {}, 'notification'),
|
('calibre.ebooks.mobi.from_feeds', 'main', {}, 'notification'),
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user