6
6
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
7
7
__docformat__ = 'restructuredtext en'
11
from lxml import etree
13
11
from sphinx.builders.epub import EpubBuilder
13
from calibre.ebooks.oeb.base import OPF, DC
14
from calibre.ebooks.oeb.polish.container import get_container, OEB_DOCS
15
from calibre.ebooks.oeb.polish.check.links import check_links, UnreferencedResource
16
from calibre.ebooks.oeb.polish.pretty import pretty_html_tree, pretty_opf
17
from calibre.utils.magick.draw import identify_data
15
19
class EPUBHelpBuilder(EpubBuilder):
18
def add_cover(self, outdir, cover_fname):
19
href = '_static/'+cover_fname
20
opf = os.path.join(self.outdir, 'content.opf')
23
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
25
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
26
<meta name="calibre:cover" content="true" />
28
<style type="text/css" title="override_css">
29
@page {padding: 0pt; margin:0pt}
30
body { text-align: center; padding:0pt; margin: 0pt; }
34
<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
35
xmlns:xlink="http://www.w3.org/1999/xlink"
36
width="100%%" height="100%%" viewBox="0 0 600 800"
37
preserveAspectRatio="none">
38
<image width="600" height="800" xlink:href="%s"/>
43
self.files.append('epub_titlepage.html')
44
open(os.path.join(outdir, self.files[-1]), 'wb').write(cover)
47
raw = open(opf, 'rb').read()
48
raw = raw.replace('</metadata>',
49
('<meta name="cover" content="%s"/>\n'
50
'<dc:date>%s</dc:date>\n</metadata>') %
51
(href.replace('/', '_'), time.strftime('%Y-%m-%d')))
52
raw = raw.replace('</manifest>',
53
('<item id="{0}" href="{0}" media-type="application/xhtml+xml"/>\n</manifest>').\
54
format('epub_titlepage.html'))
55
open(opf, 'wb').write(raw)
57
def build_epub(self, outdir, *args, **kwargs):
58
if self.config.kovid_epub_cover:
59
self.add_cover(outdir, self.config.kovid_epub_cover)
60
self.fix_duplication_bugs(outdir)
61
EpubBuilder.build_epub(self, outdir, *args, **kwargs)
63
def fix_duplication_bugs(self, outdir):
64
opf = glob.glob(outdir+os.sep+'*.opf')[0]
65
root = etree.fromstring(open(opf, 'rb').read())
68
'//*[local-name()="spine"]/*[local-name()="itemref"]'):
69
idref = x.get('idref')
71
x.getparent().remove(x)
75
with open(opf, 'wb') as f:
76
f.write(etree.tostring(root, encoding='utf-8', xml_declaration=True))
79
ncx = glob.glob(outdir+os.sep+'*.ncx')[0]
80
root = etree.fromstring(open(ncx, 'rb').read())
83
'//*[local-name()="navMap"]/*[local-name()="navPoint"]'):
84
text = x.xpath('descendant::*[local-name()="text"]')[0]
87
x.getparent().remove(x)
91
with open(ncx, 'wb') as f:
92
f.write(etree.tostring(root, encoding='utf-8', xml_declaration=True))
22
def build_epub(self, outdir, outname):
23
EpubBuilder.build_epub(self, outdir, outname)
24
container = get_container(os.path.join(outdir, outname))
25
self.fix_epub(container)
28
def fix_epub(self, container):
29
' Fix all the brokenness that sphinx\'s epub builder creates '
30
for name, mt in container.mime_map.iteritems():
32
self.workaround_ade_quirks(container, name)
33
pretty_html_tree(container, container.parsed(name))
35
self.fix_opf(container)
37
def workaround_ade_quirks(self, container, name):
38
root = container.parsed(name)
39
# ADE blows up floating images if their sizes are not specified
40
for img in root.xpath('//*[local-name() = "img" and (@class = "float-right-img" or @class = "float-left-img")]'):
41
if 'style' not in img.attrib:
42
imgname = container.href_to_name(img.get('src'), name)
43
width, height, fmt = identify_data(container.raw_data(imgname))
44
img.set('style', 'width: %dpx; height: %dpx' % (width, height))
46
def fix_opf(self, container):
47
spine_names = {n for n, l in container.spine_names}
48
spine = container.opf_xpath('//opf:spine')[0]
49
rmap = {v:k for k, v in container.manifest_id_map.iteritems()}
50
# Add unreferenced text files to the spine
51
for name, mt in container.mime_map.iteritems():
52
if mt in OEB_DOCS and name not in spine_names:
54
container.insert_into_xml(spine, spine.makeelement(OPF('itemref'), idref=rmap[name]))
56
# Remove duplicate entries from spine
58
for item, name, linear in container.spine_iter:
60
container.remove_from_xml(item)
63
# Ensure that the meta cover tag is correct
64
cover_id = rmap['_static/' + self.config.epub_cover[0]]
65
for meta in container.opf_xpath('//opf:meta[@name="cover"]'):
66
meta.set('content', cover_id)
68
# Add description metadata
69
metadata = container.opf_xpath('//opf:metadata')[0]
70
container.insert_into_xml(metadata, metadata.makeelement(DC('description')))
71
metadata[-1].text = 'Comprehensive documentation for calibre'
73
# Remove search.html since it is useless in EPUB
74
container.remove_item('search.html')
76
# Remove unreferenced files
77
for error in check_links(container):
78
if error.__class__ is UnreferencedResource:
79
container.remove_item(error.name)
81
# Pretty print the OPF
82
pretty_opf(container.parsed(container.opf_name))
83
container.dirty(container.opf_name)