From 9aa2fbfbecd0ba57226a9930dad11ab47f52daf6 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 30 Mar 2009 16:26:24 -0700 Subject: [PATCH] The complete framework for the conversion pipeline --- src/calibre/customize/conversion.py | 19 ++++++- src/calibre/ebooks/conversion/cli.py | 11 +++- src/calibre/ebooks/conversion/plumber.py | 67 +++++++++++++++++++++++- src/calibre/ebooks/oeb/output.py | 25 ++++++++- 4 files changed, 117 insertions(+), 5 deletions(-) diff --git a/src/calibre/customize/conversion.py b/src/calibre/customize/conversion.py index 3ebabc4d52..b25704569b 100644 --- a/src/calibre/customize/conversion.py +++ b/src/calibre/customize/conversion.py @@ -205,6 +205,23 @@ class OutputFormatPlugin(Plugin): #: (option_name, recommended_value, recommendation_level) recommendations = set([]) - def convert(self, oeb_book, input_plugin, options, context, log): + def convert(self, oeb_book, output, input_plugin, opts, log): + ''' + Render the contents of `oeb_book` (which is an instance of + :class:`calibre.ebooks.oeb.OEBBook` to the file specified by output. + + :param output: Either a file like object or a string. If it is a string + it is the path to a directory that may or may not exist. The output + plugin should write its output into that directory. If it is a file like + object, the output plugin should write its output into the file. + + :param input_plugin: The input plugin that was used at the beginning of + the conversion pipeline. + + :param opts: Conversion options. Guaranteed to have attributes + corresponding to the OptionRecommendations of this plugin. + + :param log: The logger. Print debug/info messages etc. using this. + ''' raise NotImplementedError diff --git a/src/calibre/ebooks/conversion/cli.py b/src/calibre/ebooks/conversion/cli.py index 211761e415..9994b61a7c 100644 --- a/src/calibre/ebooks/conversion/cli.py +++ b/src/calibre/ebooks/conversion/cli.py @@ -106,6 +106,15 @@ def add_pipeline_options(parser, plumber): 'output_profile', ] ), + 'LOOK AND FEEL' : ( + _('Options to control the look and feel of the output'), + [ + 'base_font_size', + 'font_size_mapping', + 'line_height', + 'linearize_tables', + ] + ), 'METADATA' : (_('Options to set metadata in the output'), plumber.metadata_option_names, @@ -118,7 +127,7 @@ def add_pipeline_options(parser, plumber): } - group_order = ['', 'METADATA', 'DEBUG'] + group_order = ['', 'LOOK AND FEEL', 'METADATA', 'DEBUG'] for group in group_order: desc, options = groups[group] diff --git a/src/calibre/ebooks/conversion/plumber.py b/src/calibre/ebooks/conversion/plumber.py index 0e2f98fde4..5393aaf034 100644 --- a/src/calibre/ebooks/conversion/plumber.py +++ b/src/calibre/ebooks/conversion/plumber.py @@ -68,6 +68,45 @@ OptionRecommendation(name='output_profile', ) ), +OptionRecommendation(name='base_font_size', + recommended_value=0, level=OptionRecommendation.LOW, + help=_('The base font size in pts. All font sizes in the produced book ' + 'will be rescaled based on this size. By choosing a larger ' + 'size you can make the fonts in the output bigger and vice ' + 'versa. By default, the base font size is chosen based on ' + 'the output profile you chose.' + ) + ), + +OptionRecommendation(name='font_size_mapping', + recommended_value=None, level=OptionRecommendation.LOW, + help=_('Mapping from CSS font names to font sizes in pts. ' + 'An example setting is 12,12,14,16,18,20,22,24. ' + 'These are the mappings for the sizes xx-small to xx-large, ' + 'with the final size being for huge fonts. The font ' + 'rescaling algorithm uses these sizes to intelligently ' + 'rescale fonts. The default is to use a mapping based on ' + 'the output profile you chose.' + ) + ), + +OptionRecommendation(name='line_height', + recommended_value=None, level=OptionRecommendation.LOW, + help=_('The line height in pts. Controls spacing between consecutive ' + 'lines of text. By default ??' + ) + ), + +OptionRecommendation(name='linearize_tables', + recommended_value=False, level=OptionRecommendation.LOW, + help=_('Some badly designed documents use tables to control the ' + 'layout of text on the page. When converted these documents ' + 'often have text that runs of the page and other artifacts. ' + 'This option will extract the content from the tables and ' + 'present it in a linear fashion.' + ) + ), + OptionRecommendation(name='read_metadata_from_opf', recommended_value=None, level=OptionRecommendation.LOW, short_switch='m', @@ -268,8 +307,34 @@ OptionRecommendation(name='language', self.reader = OEBReader() self.oeb = OEBBook(self.log, html_preprocessor=html_preprocessor) # Read OEB Book into OEBBook + self.log.info('Parsing all content...') self.reader(self.oeb, opfpath) - + self.opts.source = self.opts.input_profile + self.opts.dest = self.opts.output_profile + + from calibre.ebooks.oeb.transforms.flatcss import CSSFlattener + fbase = self.opts.base_font_size + if fbase == 0: + fbase = self.opts.dest.fbase + fkey = self.opts.font_size_mapping + if fkey is None: + fkey = self.opts.dest.fsizes + + flattener = CSSFlattener(fbase=fbase, fkey=fkey, + lineh=self.opts.line_height, + untable=self.opts.linearize_tables) + self.log.info('Flattening CSS...') + flattener(self.oeb, self.opts) + + from calibre.ebooks.oeb.transforms.trimmanifest import ManifestTrimmer + + self.log.info('Cleaning up manifest...') + trimmer = ManifestTrimmer() + trimmer(self.oeb, self.opts) + + self.log.info('Creating %s output...'%self.output_plugin.name) + self.output_plugin(self.oeb, self.output, self.input_plugin, self.opts, + self.log) diff --git a/src/calibre/ebooks/oeb/output.py b/src/calibre/ebooks/oeb/output.py index d8d52859eb..b26934e18a 100644 --- a/src/calibre/ebooks/oeb/output.py +++ b/src/calibre/ebooks/oeb/output.py @@ -3,7 +3,12 @@ __license__ = 'GPL 3' __copyright__ = '2009, Kovid Goyal ' __docformat__ = 'restructuredtext en' +import os + +from lxml import etree + from calibre.customize.conversion import OutputFormatPlugin +from calibre import CurrentDir class OEBOutput(OutputFormatPlugin): @@ -12,6 +17,22 @@ class OEBOutput(OutputFormatPlugin): file_type = 'oeb' - def convert(self, oeb_book, input_plugin, options, context, log): - pass + def convert(self, oeb_book, output_path, input_plugin, opts, log): + self.log, self.opts = log, opts + if not os.path.exists(output_path): + os.makedirs(output_path) + from calibre.ebooks.oeb.base import OPF_MIME, NCX_MIME, PAGE_MAP_MIME + with CurrentDir(output_path): + results = oeb_book.to_opf2(page_map=True) + for key in (OPF_MIME, NCX_MIME, PAGE_MAP_MIME): + href, root = results.pop(key, None) + if root is not None: + raw = etree.tostring(root, pretty_print=True, + encoding='utf-8') + with open(href, 'wb') as f: + f.write(raw) + + for item in oeb_book.manifest: + print item.href +