Fix various bugs in the EPUB version of the User Manual and add workarounds for proper rendering in Adobe Digital Editions

This commit is contained in:
Kovid Goyal 2014-02-13 09:51:28 +05:30
parent 62ce1a4e92
commit 7cce3562ab
4 changed files with 77 additions and 73 deletions

View File

@ -114,14 +114,13 @@ html_short_title = 'Start'
html_logo = 'resources/logo.png' html_logo = 'resources/logo.png'
epub_author = 'Kovid Goyal' epub_author = 'Kovid Goyal'
kovid_epub_cover = 'epub_cover.jpg'
epub_publisher = 'Kovid Goyal' epub_publisher = 'Kovid Goyal'
epub_identifier = 'http://manual.calibre-ebook.com' epub_identifier = 'http://manual.calibre-ebook.com'
epub_scheme = 'url' epub_scheme = 'url'
epub_uid = 'S54a88f8e9d42455e9c6db000e989225f' epub_uid = 'S54a88f8e9d42455e9c6db000e989225f'
epub_tocdepth = 4 epub_tocdepth = 4
epub_tocdup = True epub_tocdup = True
epub_pre_files = [('epub_titlepage.html', 'Cover')] epub_cover = ('epub_cover.jpg', 'epub_cover_template.html')
# Custom sidebar templates, maps document names to template names. # Custom sidebar templates, maps document names to template names.
#html_sidebars = {} #html_sidebars = {}

View File

@ -249,7 +249,6 @@ def template_docs(app):
update_cli_doc('template_ref.rst', raw, info) update_cli_doc('template_ref.rst', raw, info)
def setup(app): def setup(app):
app.add_config_value('kovid_epub_cover', None, False)
app.add_builder(EPUBHelpBuilder) app.add_builder(EPUBHelpBuilder)
app.add_builder(LaTeXHelpBuilder) app.add_builder(LaTeXHelpBuilder)
app.connect('doctree-read', substitute) app.connect('doctree-read', substitute)

View File

@ -6,89 +6,75 @@ __license__ = 'GPL v3'
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>' __copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
import os, time, glob import os
from lxml import etree
from sphinx.builders.epub import EpubBuilder from sphinx.builders.epub import EpubBuilder
from calibre.ebooks.oeb.base import OPF, DC
from calibre.ebooks.oeb.polish.container import get_container, OEB_DOCS
from calibre.ebooks.oeb.polish.check.links import check_links, UnreferencedResource
from calibre.ebooks.oeb.polish.pretty import pretty_html_tree, pretty_opf
from calibre.utils.magick.draw import identify_data
class EPUBHelpBuilder(EpubBuilder): class EPUBHelpBuilder(EpubBuilder):
name = 'myepub' name = 'myepub'
def add_cover(self, outdir, cover_fname): def build_epub(self, outdir, outname):
href = '_static/'+cover_fname EpubBuilder.build_epub(self, outdir, outname)
opf = os.path.join(self.outdir, 'content.opf') container = get_container(os.path.join(outdir, outname))
self.fix_epub(container)
container.commit()
cover = '''\ def fix_epub(self, container):
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> ' Fix all the brokenness that sphinx\'s epub builder creates '
<head> for name, mt in container.mime_map.iteritems():
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> if mt in OEB_DOCS:
<meta name="calibre:cover" content="true" /> self.workaround_ade_quirks(container, name)
<title>Cover</title> pretty_html_tree(container, container.parsed(name))
<style type="text/css" title="override_css"> container.dirty(name)
@page {padding: 0pt; margin:0pt} self.fix_opf(container)
body { text-align: center; padding:0pt; margin: 0pt; }
</style>
</head>
<body>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="100%%" height="100%%" viewBox="0 0 600 800"
preserveAspectRatio="none">
<image width="600" height="800" xlink:href="%s"/>
</svg>
</body>
</html>
'''%href
self.files.append('epub_titlepage.html')
open(os.path.join(outdir, self.files[-1]), 'wb').write(cover)
def workaround_ade_quirks(self, container, name):
root = container.parsed(name)
# ADE blows up floating images if their sizes are not specified
for img in root.xpath('//*[local-name() = "img" and (@class = "float-right-img" or @class = "float-left-img")]'):
imgname = container.href_to_name(img.get('src'), name)
width, height, fmt = identify_data(container.raw_data(imgname))
img.set('style', 'width: %dpx; height: %dpx' % (width, height))
raw = open(opf, 'rb').read() def fix_opf(self, container):
raw = raw.replace('</metadata>', spine_names = {n for n, l in container.spine_names}
('<meta name="cover" content="%s"/>\n' spine = container.opf_xpath('//opf:spine')[0]
'<dc:date>%s</dc:date>\n</metadata>') % rmap = {v:k for k, v in container.manifest_id_map.iteritems()}
(href.replace('/', '_'), time.strftime('%Y-%m-%d'))) # Add unreferenced text files to the spine
raw = raw.replace('</manifest>', for name, mt in container.mime_map.iteritems():
('<item id="{0}" href="{0}" media-type="application/xhtml+xml"/>\n</manifest>').\ if mt in OEB_DOCS and name not in spine_names:
format('epub_titlepage.html')) spine_names.add(name)
open(opf, 'wb').write(raw) container.insert_into_xml(spine, spine.makeelement(OPF('itemref'), idref=rmap[name]))
def build_epub(self, outdir, *args, **kwargs): # Remove duplicate entries from spine
if self.config.kovid_epub_cover:
self.add_cover(outdir, self.config.kovid_epub_cover)
self.fix_duplication_bugs(outdir)
EpubBuilder.build_epub(self, outdir, *args, **kwargs)
def fix_duplication_bugs(self, outdir):
opf = glob.glob(outdir+os.sep+'*.opf')[0]
root = etree.fromstring(open(opf, 'rb').read())
seen = set() seen = set()
for x in root.xpath( for item, name, linear in container.spine_iter:
'//*[local-name()="spine"]/*[local-name()="itemref"]'): if name in seen:
idref = x.get('idref') container.remove_from_xml(item)
if idref in seen: seen.add(name)
x.getparent().remove(x)
else:
seen.add(idref)
with open(opf, 'wb') as f: # Ensure that the meta cover tag is correct
f.write(etree.tostring(root, encoding='utf-8', xml_declaration=True)) cover_id = rmap['_static/' + self.config.epub_cover[0]]
for meta in container.opf_xpath('//opf:meta[@name="cover"]'):
meta.set('content', cover_id)
# Add description metadata
metadata = container.opf_xpath('//opf:metadata')[0]
container.insert_into_xml(metadata, metadata.makeelement(DC('description')))
metadata[-1].text = 'Comprehensive documentation for calibre'
ncx = glob.glob(outdir+os.sep+'*.ncx')[0] # Remove unreferenced files
root = etree.fromstring(open(ncx, 'rb').read()) for error in check_links(container):
seen = set() if error.__class__ is UnreferencedResource:
for x in root.xpath( container.remove_item(error.name)
'//*[local-name()="navMap"]/*[local-name()="navPoint"]'):
text = x.xpath('descendant::*[local-name()="text"]')[0]
text = text.text
if text in seen:
x.getparent().remove(x)
else:
seen.add(text)
with open(ncx, 'wb') as f:
f.write(etree.tostring(root, encoding='utf-8', xml_declaration=True))
# Pretty print the OPF
pretty_opf(container.parsed(container.opf_name))
container.dirty(container.opf_name)

View File

@ -0,0 +1,20 @@
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="calibre:cover" content="true" />
<title>{{ title }}</title>
<style type="text/css" title="override_css">
@page {padding: 0pt; margin:0pt}
body { text-align: center; padding:0pt; margin: 0pt; }
</style>
</head>
<body>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="100%" height="100%" viewBox="0 0 600 800"
preserveAspectRatio="none">
<image width="600" height="800" xlink:href="{{ pathto('_static/' + image, 1) }}"/>
</svg>
</body>
</html>