mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Switch to Sphinx 1.0 to generate User Manual
This commit is contained in:
parent
a69641ac1b
commit
48df7c38bd
@ -73,11 +73,11 @@ class Manual(Command):
|
||||
os.makedirs(d)
|
||||
if not os.path.exists('.build'+os.sep+'html'):
|
||||
os.makedirs('.build'+os.sep+'html')
|
||||
os.environ['__appname__']= __appname__
|
||||
os.environ['__version__']= __version__
|
||||
subprocess.check_call(['sphinx-build', '-b', 'custom', '-t', 'online',
|
||||
os.environ['__appname__'] = __appname__
|
||||
os.environ['__version__'] = __version__
|
||||
subprocess.check_call(['sphinx-build', '-b', 'html', '-t', 'online',
|
||||
'-d', '.build/doctrees', '.', '.build/html'])
|
||||
subprocess.check_call(['sphinx-build', '-b', 'epub', '-d',
|
||||
subprocess.check_call(['sphinx-build', '-b', 'myepub', '-d',
|
||||
'.build/doctrees', '.', '.build/epub'])
|
||||
shutil.copyfile(self.j('.build', 'epub', 'calibre.epub'), self.j('.build',
|
||||
'html', 'calibre.epub'))
|
||||
|
@ -37,7 +37,7 @@ qthelp:
|
||||
|
||||
epub:
|
||||
mkdir -p .build/qthelp .build/doctrees
|
||||
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) .build/epub
|
||||
$(SPHINXBUILD) -b myepub $(ALLSPHINXOPTS) .build/epub
|
||||
@echo
|
||||
@echo "Build finished."
|
||||
|
||||
|
@ -23,9 +23,11 @@ custom
|
||||
# General configuration
|
||||
# ---------------------
|
||||
|
||||
needs_sphinx = '1.0'
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be extensions
|
||||
# coming with Sphinx (named 'sphinx.addons.*') or your custom ones.
|
||||
extensions = ['sphinx.ext.autodoc', 'custom']
|
||||
extensions = ['sphinx.ext.autodoc', 'custom', 'sphinx.ext.viewcode']
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['templates']
|
||||
@ -36,6 +38,9 @@ source_suffix = '.rst'
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# The language
|
||||
language = 'en'
|
||||
|
||||
# General substitutions.
|
||||
project = __appname__
|
||||
copyright = '2008, Kovid Goyal'
|
||||
@ -81,7 +86,6 @@ pygments_style = 'sphinx'
|
||||
# given in html_static_path.
|
||||
html_theme = 'default'
|
||||
html_theme_options = {'stickysidebar':'true', 'relbarbgcolor':'black'}
|
||||
html_style = 'calibre.css'
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
@ -100,8 +104,16 @@ html_use_smartypants = True
|
||||
html_title = 'calibre User Manual'
|
||||
html_short_title = 'Start'
|
||||
html_logo = 'resources/logo.png'
|
||||
|
||||
epub_author = 'Kovid Goyal'
|
||||
epub_cover = 'resources/epub_cover.jpg'
|
||||
epub_cover = 'epub_cover.jpg'
|
||||
epub_publisher = 'Kovid Goyal'
|
||||
epub_identifier = 'http://calibre-ebook.com/user_manual'
|
||||
epub_scheme = 'url'
|
||||
epub_uid = 'S54a88f8e9d42455e9c6db000e989225f'
|
||||
epub_tocdepth = 4
|
||||
epub_tocdup = True
|
||||
epub_pre_files = [('epub_titlepage.html', 'Cover')]
|
||||
|
||||
# Custom sidebar templates, maps document names to template names.
|
||||
#html_sidebars = {}
|
||||
|
@ -9,9 +9,6 @@ sys.path.insert(0, os.path.abspath('../../'))
|
||||
sys.extensions_location = '../plugins'
|
||||
sys.resources_location = '../../../resources'
|
||||
|
||||
from sphinx.builders.html import StandaloneHTMLBuilder
|
||||
from qthelp import QtHelpBuilder
|
||||
from epub import EPUBHelpBuilder
|
||||
from sphinx.util import rpartition
|
||||
from sphinx.util.console import bold
|
||||
from sphinx.ext.autodoc import prepare_docstring
|
||||
@ -20,12 +17,7 @@ from docutils import nodes
|
||||
|
||||
sys.path.append(os.path.abspath('../../../'))
|
||||
from calibre.linux import entry_points
|
||||
|
||||
class CustomBuilder(StandaloneHTMLBuilder):
|
||||
name = 'custom'
|
||||
|
||||
class CustomQtBuild(QtHelpBuilder):
|
||||
name = 'customqt'
|
||||
from epub import EPUBHelpBuilder
|
||||
|
||||
def substitute(app, doctree):
|
||||
pass
|
||||
@ -305,9 +297,6 @@ def auto_member(dirname, arguments, options, content, lineno,
|
||||
|
||||
def setup(app):
|
||||
app.add_config_value('epub_cover', None, False)
|
||||
app.add_config_value('epub_author', '', False)
|
||||
app.add_builder(CustomBuilder)
|
||||
app.add_builder(CustomQtBuild)
|
||||
app.add_builder(EPUBHelpBuilder)
|
||||
app.add_directive('automember', auto_member, 1, (1, 0, 1))
|
||||
app.connect('doctree-read', substitute)
|
||||
|
@ -6,298 +6,53 @@ __license__ = 'GPL v3'
|
||||
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
import os, mimetypes, uuid, shutil
|
||||
from datetime import datetime
|
||||
from docutils import nodes
|
||||
from xml.sax.saxutils import escape, quoteattr
|
||||
from urlparse import urldefrag
|
||||
from zipfile import ZipFile, ZIP_STORED, ZipInfo
|
||||
import os, time
|
||||
|
||||
from sphinx import addnodes
|
||||
from sphinx.builders.html import StandaloneHTMLBuilder
|
||||
from sphinx.builders.epub import EpubBuilder
|
||||
|
||||
NCX = '''\
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ncx version="2005-1"
|
||||
xml:lang="en"
|
||||
xmlns="http://www.daisy.org/z3986/2005/ncx/"
|
||||
xmlns:calibre="http://calibre.kovidgoyal.net/2009/metadata"
|
||||
>
|
||||
<head>
|
||||
<meta name="dtb:uid" content="{uid}"/>
|
||||
<meta name="dtb:depth" content="{depth}"/>
|
||||
<meta name="dtb:generator" content="sphinx"/>
|
||||
<meta name="dtb:totalPageCount" content="0"/>
|
||||
<meta name="dtb:maxPageNumber" content="0"/>
|
||||
</head>
|
||||
<docTitle><text>Table of Contents</text></docTitle>
|
||||
<navMap>
|
||||
{navpoints}
|
||||
</navMap>
|
||||
</ncx>
|
||||
'''
|
||||
class EPUBHelpBuilder(EpubBuilder):
|
||||
name = 'myepub'
|
||||
|
||||
OPF = '''\
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<package version="2.0"
|
||||
xmlns="http://www.idpf.org/2007/opf"
|
||||
unique-identifier="sphinx_id"
|
||||
>
|
||||
<metadata xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:opf="http://www.idpf.org/2007/opf" xmlns:calibre="http://calibre.kovidgoyal.net/2009/metadata">
|
||||
<dc:title>{title}</dc:title>
|
||||
<dc:creator opf:role="aut">{author}</dc:creator>
|
||||
<dc:contributor opf:role="bkp">Sphinx</dc:contributor>
|
||||
<dc:identifier opf:scheme="sphinx" id="sphinx_id">{uid}</dc:identifier>
|
||||
<dc:date>{date}</dc:date>
|
||||
<meta name="calibre:publication_type" content="sphinx_manual" />
|
||||
<meta name="cover" content="cover"/>
|
||||
</metadata>
|
||||
<manifest>
|
||||
{manifest}
|
||||
</manifest>
|
||||
<spine toc="ncx">
|
||||
{spine}
|
||||
</spine>
|
||||
<guide>
|
||||
{guide}
|
||||
</guide>
|
||||
</package>
|
||||
'''
|
||||
def add_cover(self, outdir, cover_fname):
|
||||
href = '_static/'+cover_fname
|
||||
opf = os.path.join(self.outdir, 'content.opf')
|
||||
|
||||
CONTAINER='''\
|
||||
<?xml version="1.0"?>
|
||||
<container version="1.0" xmlns="urn:oasis:names:tc:opendocument:xmlns:container">
|
||||
<rootfiles>
|
||||
<rootfile full-path="{0}" media-type="application/oebps-package+xml"/>
|
||||
</rootfiles>
|
||||
</container>
|
||||
'''
|
||||
|
||||
SVG_TEMPLATE = '''\
|
||||
<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>Cover</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="%s"/>
|
||||
</svg>
|
||||
</body>
|
||||
</html>
|
||||
'''
|
||||
|
||||
class TOC(list):
|
||||
|
||||
def __init__(self, title=None, href=None):
|
||||
list.__init__(self)
|
||||
self.title, self.href = title, href
|
||||
|
||||
def create_child(self, title, href):
|
||||
self.append(TOC(title, href))
|
||||
return self[-1]
|
||||
|
||||
def depth(self):
|
||||
try:
|
||||
return max(node.depth() for node in self)+1
|
||||
except ValueError:
|
||||
return 1
|
||||
cover = '''\
|
||||
<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>Cover</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="%s"/>
|
||||
</svg>
|
||||
</body>
|
||||
</html>
|
||||
'''%href
|
||||
self.files.append('epub_titlepage.html')
|
||||
open(os.path.join(outdir, self.files[-1]), 'wb').write(cover)
|
||||
|
||||
|
||||
class EPUBHelpBuilder(StandaloneHTMLBuilder):
|
||||
"""
|
||||
Builder that also outputs Qt help project, contents and index files.
|
||||
"""
|
||||
name = 'epub'
|
||||
|
||||
# don't copy the reST source
|
||||
copysource = False
|
||||
|
||||
supported_image_types = ['image/svg+xml', 'image/png', 'image/gif',
|
||||
'image/jpeg']
|
||||
|
||||
# don't add links
|
||||
add_permalinks = False
|
||||
# don't add sidebar etc.
|
||||
embedded = True
|
||||
|
||||
def init(self):
|
||||
StandaloneHTMLBuilder.init(self)
|
||||
self.out_suffix = '.html'
|
||||
self.link_suffix = '.html'
|
||||
self.html_outdir = self.outdir = os.path.join(self.outdir, 'src')
|
||||
self.conf = self.config
|
||||
|
||||
def finish(self):
|
||||
StandaloneHTMLBuilder.finish(self)
|
||||
self.create_titlepage()
|
||||
self.outdir = os.path.dirname(self.outdir)
|
||||
cwd = os.getcwd()
|
||||
os.chdir(self.html_outdir)
|
||||
try:
|
||||
self.generate_manifest()
|
||||
self.generate_toc()
|
||||
self.render_opf()
|
||||
self.render_epub()
|
||||
finally:
|
||||
os.chdir(cwd)
|
||||
|
||||
def render_epub(self):
|
||||
container = CONTAINER.format('content.opf')
|
||||
path = os.path.abspath('..'+os.sep+self.conf.project+'.epub')
|
||||
zf = ZipFile(path, 'w')
|
||||
zi = ZipInfo('mimetype')
|
||||
zi.compress_type = ZIP_STORED
|
||||
zf.writestr(zi, 'application/epub+zip')
|
||||
zf.writestr('META-INF/container.xml', container)
|
||||
for url in self.manifest:
|
||||
fp = os.path.join(self.html_outdir, *url.split('/'))
|
||||
zf.write(fp, url)
|
||||
zf.close()
|
||||
self.info('EPUB created at: '+path)
|
||||
|
||||
|
||||
def render_opf(self):
|
||||
manifest = []
|
||||
for href in self.manifest:
|
||||
mt, id = self.manifest[href]
|
||||
manifest.append(' '*8 + '<item id=%s href=%s media-type=%s />'%\
|
||||
tuple(map(quoteattr, (id, href, mt))))
|
||||
manifest = '\n'.join(manifest)
|
||||
spine = [' '*8+'<itemref idref=%s />'%quoteattr(x) for x in self.spine]
|
||||
spine = '\n'.join(spine)
|
||||
guide = ''
|
||||
|
||||
opf = OPF.format(title=escape(self.conf.html_title),
|
||||
author=escape(self.conf.epub_author), uid=str(uuid.uuid4()),
|
||||
date=datetime.now().isoformat(), manifest=manifest, spine=spine,
|
||||
guide=guide)
|
||||
open('content.opf', 'wb').write(opf)
|
||||
self.manifest['content.opf'] = ('application/oebps-package+xml', 'opf')
|
||||
|
||||
def create_titlepage(self):
|
||||
self.cover_image_url = None
|
||||
if self.conf.epub_cover:
|
||||
img = '_static/'+os.path.basename(self.conf.epub_cover)
|
||||
shutil.copyfile(self.conf.epub_cover, os.path.join(self.html_outdir,
|
||||
*img.split('/')))
|
||||
self.cover_image_url = img
|
||||
tp = SVG_TEMPLATE%img.split('/')[-1]
|
||||
open(os.path.join(self.html_outdir, '_static', 'titlepage.html'),
|
||||
'wb').write(tp)
|
||||
|
||||
def generate_manifest(self):
|
||||
self.manifest = {}
|
||||
id = 1
|
||||
for dirpath, dirnames, filenames in os.walk('.'):
|
||||
for fname in filenames:
|
||||
if fname == '.buildinfo':
|
||||
continue
|
||||
fpath = os.path.abspath(os.path.join(dirpath, fname))
|
||||
url = os.path.relpath(fpath).replace(os.sep, '/')
|
||||
self.manifest[url] = mimetypes.guess_type(url, False)[0]
|
||||
if self.manifest[url] is None:
|
||||
self.warn('Unknown mimetype for: ' + url)
|
||||
self.manifest[url] = 'application/octet-stream'
|
||||
if self.manifest[url] == 'text/html':
|
||||
self.manifest[url] = 'application/xhtml+xml'
|
||||
if self.cover_image_url and url.endswith(self.cover_image_url):
|
||||
id_ = 'cover'
|
||||
else:
|
||||
id_ = 'id'+str(id)
|
||||
id += 1
|
||||
self.manifest[url] = (self.manifest[url], id_)
|
||||
|
||||
def isdocnode(self, node):
|
||||
if not isinstance(node, nodes.list_item):
|
||||
return False
|
||||
if len(node.children) != 2:
|
||||
return False
|
||||
if not isinstance(node.children[0], addnodes.compact_paragraph):
|
||||
return False
|
||||
if not isinstance(node.children[0][0], nodes.reference):
|
||||
return False
|
||||
if not isinstance(node.children[1], nodes.bullet_list):
|
||||
return False
|
||||
return True
|
||||
|
||||
def generate_toc(self):
|
||||
tocdoc = self.env.get_and_resolve_doctree(self.config.master_doc, self,
|
||||
prune_toctrees=False)
|
||||
istoctree = lambda node: (
|
||||
isinstance(node, addnodes.compact_paragraph)
|
||||
and node.has_key('toctree'))
|
||||
toc = TOC()
|
||||
for node in tocdoc.traverse(istoctree):
|
||||
self.extend_toc(toc, node)
|
||||
self._parts = []
|
||||
self._po = 0
|
||||
self._po_map = {}
|
||||
self.spine_map = {}
|
||||
self.spine = []
|
||||
self.render_toc(toc)
|
||||
navpoints = '\n'.join(self._parts).strip()
|
||||
ncx = NCX.format(uid=str(uuid.uuid4()), depth=toc.depth(),
|
||||
navpoints=navpoints)
|
||||
open('toc.ncx', 'wb').write(ncx)
|
||||
self.manifest['toc.ncx'] = ('application/x-dtbncx+xml', 'ncx')
|
||||
self.spine.insert(0, self.manifest[self.conf.master_doc+'.html'][1])
|
||||
if self.conf.epub_cover:
|
||||
self.spine.insert(0, self.manifest['_static/titlepage.html'][1])
|
||||
|
||||
def add_to_spine(self, href):
|
||||
href = urldefrag(href)[0]
|
||||
if href not in self.spine_map:
|
||||
for url in self.manifest:
|
||||
if url == href:
|
||||
self.spine_map[href]= self.manifest[url][1]
|
||||
self.spine.append(self.spine_map[href])
|
||||
|
||||
def render_toc(self, toc, level=2):
|
||||
for child in toc:
|
||||
if child.title and child.href:
|
||||
href = child.href
|
||||
self.add_to_spine(href)
|
||||
title = escape(child.title)
|
||||
if isinstance(title, unicode):
|
||||
title = title.encode('utf-8')
|
||||
if child.href in self._po_map:
|
||||
po = self._po_map[child.href]
|
||||
else:
|
||||
self._po += 1
|
||||
po = self._po
|
||||
self._parts.append(' '*(level*4)+
|
||||
'<navPoint id="%s" playOrder="%d">'%(uuid.uuid4(),
|
||||
po))
|
||||
self._parts.append(' '*((level+1)*4)+
|
||||
'<navLabel><text>%s</text></navLabel>'%title)
|
||||
self._parts.append(' '*((level+1)*4)+
|
||||
'<content src=%s />'%quoteattr(href))
|
||||
self.render_toc(child, level+1)
|
||||
self._parts.append(' '*(level*4)+'</navPoint>')
|
||||
|
||||
|
||||
|
||||
|
||||
def extend_toc(self, toc, node):
|
||||
if self.isdocnode(node):
|
||||
refnode = node.children[0][0]
|
||||
parent = toc.create_child(refnode.astext(), refnode['refuri'])
|
||||
for subnode in node.children[1]:
|
||||
self.extend_toc(parent, subnode)
|
||||
elif isinstance(node, (nodes.list_item, nodes.bullet_list,
|
||||
addnodes.compact_paragraph)):
|
||||
for subnode in node:
|
||||
self.extend_toc(toc, subnode)
|
||||
elif isinstance(node, nodes.reference):
|
||||
parent = toc.create_child(node.astext(), node['refuri'])
|
||||
|
||||
raw = open(opf, 'rb').read()
|
||||
raw = raw.replace('</metadata>',
|
||||
('<meta name="cover" content="%s"/>\n'
|
||||
'<dc:date>%s</dc:date>\n</metadata>') %
|
||||
(href.replace('/', '_'), time.strftime('%Y-%m-%d')))
|
||||
raw = raw.replace('</manifest>',
|
||||
('<item id="{0}" href="{0}" media-type="application/xhtml+xml"/>\n</manifest>').\
|
||||
format('epub_titlepage.html'))
|
||||
open(opf, 'wb').write(raw)
|
||||
|
||||
def build_epub(self, outdir, *args, **kwargs):
|
||||
if self.config.epub_cover:
|
||||
self.add_cover(outdir, self.config.epub_cover)
|
||||
EpubBuilder.build_epub(self, outdir, *args, **kwargs)
|
||||
|
@ -1,5 +0,0 @@
|
||||
|
||||
@import url("default.css");
|
||||
|
||||
table.docutils td, table.docutils th { padding: 1em; border-bottom: 0; }
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 9.0 KiB After Width: | Height: | Size: 10 KiB |
Loading…
x
Reference in New Issue
Block a user