diff --git a/src/calibre/ebooks/epub/output.py b/src/calibre/ebooks/epub/output.py index 71d9d8b423..47d06c2255 100644 --- a/src/calibre/ebooks/epub/output.py +++ b/src/calibre/ebooks/epub/output.py @@ -7,12 +7,10 @@ __copyright__ = '2009, Kovid Goyal ' __docformat__ = 'restructuredtext en' import os, shutil, re -from urllib import unquote from calibre.customize.conversion import OutputFormatPlugin from calibre.ptempfile import TemporaryDirectory -from calibre.constants import __appname__, __version__ -from calibre import guess_type, CurrentDir +from calibre import CurrentDir from calibre.customize.conversion import OptionRecommendation from calibre.constants import filesystem_encoding @@ -46,155 +44,8 @@ block_level_tags = ( 'ul', ) -class CoverManager(object): - ''' - 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 = '''\ - - - - - Cover - - - -
- cover -
- - - ''' - - TITLEPAGE_COVER = '''\ - - - - - Cover - - - - - - - - -''' - - 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): +class EPUBOutput(OutputFormatPlugin): name = 'EPUB Output' author = 'Kovid Goyal' @@ -284,7 +135,12 @@ class EPUBOutput(OutputFormatPlugin, CoverManager): ) split(self.oeb, self.opts) - self.insert_cover() + from calibre.ebooks.oeb.transforms.cover import CoverManager + cm = CoverManager( + no_default_cover=self.opts.no_default_epub_cover, + no_svg_cover=self.opts.no_svg_cover, + preserve_aspect_ratio=self.opts.preserve_cover_aspect_ratio) + cm(self.oeb, self.opts, self.log) self.workaround_sony_quirks() diff --git a/src/calibre/ebooks/oeb/iterator.py b/src/calibre/ebooks/oeb/iterator.py index 69f7b7fe4e..020cf8d202 100644 --- a/src/calibre/ebooks/oeb/iterator.py +++ b/src/calibre/ebooks/oeb/iterator.py @@ -18,10 +18,10 @@ from calibre.ebooks.chardet import xml_to_unicode from calibre.utils.zipfile import safe_replace, ZipFile from calibre.utils.config import DynamicConfig from calibre.utils.logging import Log -from calibre.ebooks.epub.output import EPUBOutput from calibre import guess_type, prints +from calibre.ebooks.oeb.transforms.cover import CoverManager -TITLEPAGE = EPUBOutput.TITLEPAGE_COVER.decode('utf-8') +TITLEPAGE = CoverManager.SVG_TEMPLATE.decode('utf-8').replace('__ar__', 'none') def character_count(html): ''' diff --git a/src/calibre/ebooks/oeb/transforms/cover.py b/src/calibre/ebooks/oeb/transforms/cover.py new file mode 100644 index 0000000000..9aee46c591 --- /dev/null +++ b/src/calibre/ebooks/oeb/transforms/cover.py @@ -0,0 +1,173 @@ +#!/usr/bin/env python +# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai + +__license__ = 'GPL v3' +__copyright__ = '2010, Kovid Goyal ' +__docformat__ = 'restructuredtext en' + +import textwrap +from urllib import unquote + +from lxml import etree + +from calibre import __appname__, __version__, guess_type + +class CoverManager(object): + + SVG_TEMPLATE = textwrap.dedent('''\ + + + + + Cover + + + + + + + + + ''') + + NONSVG_TEMPLATE = textwrap.dedent('''\ + + + + + Cover + + + +
+ cover +
+ + + ''') + + + def __init__(self, no_default_cover=False, no_svg_cover=False, + preserve_aspect_ratio=False, fixed_size=None): + self.no_default_cover = no_default_cover + self.no_svg_cover = no_svg_cover + self.preserve_aspect_ratio = preserve_aspect_ratio + + ar = 'xMidYMid meet' if preserve_aspect_ratio else 'none' + self.svg_template = self.SVG_TEMPLATE.replace('__ar__', ar) + + if fixed_size is None: + style = 'style="height: 100%%"' + else: + width, height = fixed_size + style = 'style="height: %s; width: %s"'%(width, height) + self.non_svg_template = self.NONSVG_TEMPLATE.replace('__style__', + style) + + def __call__(self, oeb, opts, log): + self.oeb = oeb + self.log = log + self.insert_cover() + + 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.no_default_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 + 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.non_svg_template if self.no_svg_cover \ + else self.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 + + diff --git a/src/calibre/manual/conversion.rst b/src/calibre/manual/conversion.rst index acaf5ab238..ee148c79c7 100644 --- a/src/calibre/manual/conversion.rst +++ b/src/calibre/manual/conversion.rst @@ -491,9 +491,9 @@ TXT input supports a number of options to differentiate how paragraphs are detec :guilabel:`Process using markdown` |app| also supports running TXT input though a transformation preprocessor known as markdown. Markdown allows for basic formatting to be added to TXT documents, such as bold, italics, section headings, tables, - loists, a Table of Contents, etc. Marking chapter headings with a leading # and setting the chapter XPath detection + lists, a Table of Contents, etc. Marking chapter headings with a leading # and setting the chapter XPath detection expression to "//h:h1" is the easiest way to have a proper table of contents generated from a TXT document. - You can learn more about the markdown syntax at http://daringfireball.net/projects/markdown/syntax. + You can learn more about the markdown syntax `here `_. Convert PDF documents