diff --git a/doc/Makefile b/doc/Makefile
index 07cee7449..ffd51dc64 100644
--- a/doc/Makefile
+++ b/doc/Makefile
@@ -19,6 +19,7 @@ help:
@echo " dirhtml to make HTML files called index.html in directories"
@echo " pickle to make pickle files"
@echo " htmlhelp to make HTML files and a HTML help project"
+ @echo " epub to make an epub file"
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
@echo " changes to make an overview over all changed/added/deprecated items"
@echo " linkcheck to check all external links for integrity"
@@ -65,6 +66,11 @@ qthelp:
@echo "To view the help collection:"
@echo "# assistant -collectionFile _build/qthelp/Sphinx.qhc"
+epub:
+ mkdir -p _build/epub _build/doctrees
+ $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) _build/epub
+ @echo
+ @echo "Build finished. The epub file is in _build/epub."
latex:
mkdir -p _build/latex _build/doctrees
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) _build/latex
diff --git a/doc/_templates/layout.html b/doc/_templates/layout.html
index aa33576a7..e41b68b17 100644
--- a/doc/_templates/layout.html
+++ b/doc/_templates/layout.html
@@ -1,10 +1,12 @@
{% extends "!layout.html" %}
{% block extrahead %}
+{%- if not embedded %}
+{% endif %}
{% endblock %}
{% block rootrellink %}
diff --git a/doc/builders.rst b/doc/builders.rst
index 7c1f03d3d..3ce8f56a7 100644
--- a/doc/builders.rst
+++ b/doc/builders.rst
@@ -63,6 +63,18 @@ The builder's "name" must be given to the **-b** command-line option of
Its name is ``devhelp``.
+.. module:: sphinx.builders.epub
+.. class:: EpubBuilder
+
+ This builder produces the same output as the standalone HTML builder, but
+ also generates an *epub* file for ebook readers.
+ This builder is meant to be used together with the
+ :confval:`html_theme` ``'epub'``.
+ See ``_ or
+ ``_ for the definition of epubs.
+
+ Its name is ``epub``.
+
.. module:: sphinx.builders.latex
.. class:: LaTeXBuilder
diff --git a/doc/conf.py b/doc/conf.py
index e3952a0b2..7393643a1 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -34,7 +34,7 @@ release = version
show_authors = True
# The HTML template theme.
-html_theme = 'sphinxdoc'
+html_theme = 'epub'
# A list of ignored prefixes names for module index sorting.
modindex_common_prefix = ['sphinx.']
@@ -64,6 +64,18 @@ html_use_opensearch = 'http://sphinx.pocoo.org'
# Output file base name for HTML help builder.
htmlhelp_basename = 'Sphinxdoc'
+# Epub fields
+epub_basename = 'sphinx'
+epub_author = 'Georg Brandl'
+epub_publisher = 'http://sphinx.pocoo.org/'
+epub_scheme = 'url'
+epub_identifier = epub_publisher
+epub_pre_files = [ ('index', 'Welcome')]
+epub_exclude_files = ['_static/opensearch.xml', '_static/doctools.js',
+ '_static/jquery.js', '_static/searchtools.js',
+ '_static/basic.css', 'search.html']
+
+
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [('contents', 'sphinx.tex', 'Sphinx Documentation',
diff --git a/doc/config.rst b/doc/config.rst
index 3ce8d53bb..99b484c85 100644
--- a/doc/config.rst
+++ b/doc/config.rst
@@ -549,6 +549,99 @@ that use Sphinx' HTMLWriter class.
Output file base name for HTML help builder. Default is ``'pydoc'``.
+.. _epub-options:
+
+Options for epub output
+-----------------------
+
+These options influence the epub output. As this writer derives from the
+HTMLWriter the HTML options also apply where appropriate.
+The actual values for some of the options is not really important,
+they just have to be entered into
+the `Dublin Core metadata `_.
+
+.. confval:: epub_basename
+
+ The basename for the epub file. It defaults to the :confval:`project` name.
+
+.. confval:: epub_title
+
+ The title of the document.
+ It defaults to the :confval:`html_title` option
+ but can be set independently for epub creation.
+
+.. confval:: epub_author
+
+ The author of the document.
+ This is put in the Dublin Core metadata.
+ The default value is ``'unknown'``.
+
+.. confval:: epub_language
+
+ The language of the document.
+ This is put in the Dublin Core metadata.
+ The default is the :confval:`language` option or ``'en'`` if unset.
+
+.. confval:: epub_publisher
+
+ The publisher of the document.
+ This is put in the Dublin Core metadata.
+ You may use any sensible string, e.g. the project homepage.
+ The default value is ``'unknown'``.
+
+.. confval:: epub_copyright
+
+ The copyright of the document.
+ It defaults to the :confval:`copyright` option
+ but can be set independently for epub creation.
+
+.. confval:: epub_identifier
+
+ An identifier for the document.
+ This is put in the Dublin Core metadata.
+ For published documents this is the ISBN number, but you can
+ also use an alternative scheme, e.g. the project homepage.
+ The default value is ``'unknown'``.
+
+.. confval:: epub_scheme
+
+ The publication scheme for the :confval:`epub_identifier`.
+ This is put in the Dublin Core metadata.
+ For published books the scheme is ``'ISBN'``.
+ If you use the project homepage, ``'URL'`` seems reasonable.
+
+.. confval:: epub_uid
+
+ A unique identifier for the document.
+ This is put in the Dublin Core metadata.
+ You may use a random string.
+ The default value is ``'unknown'``.
+
+.. confval:: epub_pre_files
+
+ Additional files that should be inserted before the text generated by
+ Sphinx. It is a list of tuples containing the file name and the title.
+ Example::
+
+ epub_pre_files = [
+ ('index.html', 'Welcome'),
+ ]
+
+ The default value is ``[]``.
+
+.. confval:: epub_post_files
+
+ Additional files that should be inserted after the text generated by
+ Sphinx. It is a list of tuples containing the file name and the title.
+ The default value is ``[]``.
+
+.. confval:: epub_exclude_files
+
+ A list of files that are generated/copied in the build directory
+ but should not be included in the epub file.
+ The default value is ``[]``.
+
+
.. _latex-options:
Options for LaTeX output
diff --git a/doc/faq.rst b/doc/faq.rst
index 34502dc41..46a714dd0 100644
--- a/doc/faq.rst
+++ b/doc/faq.rst
@@ -54,5 +54,46 @@ github pages
Sphinx HTML output.
+Epub
+----
+
+The EpubBuilder is currently in an experimental stage.
+It has only been tested with the Sphinx documentation itself.
+If you want to create epubs, here are some notes:
+
+* Split the text into several files. The longer the individual HTML files
+ are, the longer it takes the ebook reader to render them.
+ In extreme cases, the rendering can take up to one minute.
+
+* Try to minimize the markup. This also pays in rendering time.
+
+* For some readers you can use embedded or external fonts
+ using the CSS ``@font-face`` directive.
+ This is *extremely* useful for code listings which are often cut
+ at the right margin. The default Courier font (or variant) is quite
+ wide and you can only display up to 60 characters on a line.
+ If you replace it with a narrower font, you can get more characters
+ on a line. You may even use
+ `fontforge `_
+ and create narrow variants
+ of some free font. In my case I get up to 70 characters on a line.
+
+ You may have to experiment a little until you get reasonable results.
+
+* Test the created epubs. You can use several alternatives.
+ The ones I am aware of are
+ Epubcheck
+ (`http://code.google.com/p/epubcheck/
+ `_),
+ Calibre
+ (`http://calibre-ebook.com/ `_),
+ FBreader (`http://www.fbreader.org/ `_,
+ although it does not render the CSS), and
+ Bookworm (`http://bookworm.oreilly.com/ `_).
+ For bookworm you can download the source from
+ `http://code.google.com/p/threepress/ `_
+ and run you own local server.
+
+
.. _api role: http://git.savannah.gnu.org/cgit/kenozooid.git/tree/doc/extapi.py
.. _xhtml to reST: http://docutils.sourceforge.net/sandbox/xhtml2rest/xhtml2rest.py
diff --git a/doc/theming.rst b/doc/theming.rst
index 7af54a0be..4f7cadd1d 100644
--- a/doc/theming.rst
+++ b/doc/theming.rst
@@ -160,6 +160,10 @@ These themes are:
* **traditional** -- A theme resembling the old Python documentation. There are
currently no options beyond *nosidebar*.
+* **epub** -- A theme for the epub formatter. There are currently no
+ options. This theme tries to reduce visual space which is a sparse
+ resource on ebook readers.
+
Creating themes
---------------
diff --git a/sphinx/builders/__init__.py b/sphinx/builders/__init__.py
index 9ca35cc38..a871cf235 100644
--- a/sphinx/builders/__init__.py
+++ b/sphinx/builders/__init__.py
@@ -388,6 +388,7 @@ BUILTIN_BUILDERS = {
'htmlhelp': ('htmlhelp', 'HTMLHelpBuilder'),
'devhelp': ('devhelp', 'DevhelpBuilder'),
'qthelp': ('qthelp', 'QtHelpBuilder'),
+ 'epub': ('epub', 'EpubBuilder'),
'latex': ('latex', 'LaTeXBuilder'),
'text': ('text', 'TextBuilder'),
'changes': ('changes', 'ChangesBuilder'),
diff --git a/sphinx/builders/epub.py b/sphinx/builders/epub.py
new file mode 100644
index 000000000..57e2dff9a
--- /dev/null
+++ b/sphinx/builders/epub.py
@@ -0,0 +1,409 @@
+# -*- coding: utf-8 -*-
+"""
+ sphinx.builders.epub
+ ~~~~~~~~~~~~~~~~~~~~
+
+ Build epub files.
+ Originally derived from qthelp.py.
+
+ :copyright: Copyright 2007-2009 by the Sphinx team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+import os
+import re
+import codecs
+from os import path
+import zipfile
+
+from docutils import nodes
+
+from sphinx import addnodes
+from sphinx.builders.html import StandaloneHTMLBuilder
+
+
+# (Fragment) templates from which the metainfo files
+# content.opf, toc.ncx, mimetype, and META-INF/container.xml
+# are created.
+
+_mimetype_template = 'application/epub+zip' # no EOL!
+
+_container_template = u'''\
+
+
+
+
+
+
+'''
+
+_toc_template = u'''\
+
+
+
+
+
+
+
+
+
+ %(title)s
+
+
+%(navpoints)s
+
+
+'''
+
+_navpoint_template = u'''\
+%(indent)s
+%(indent)s
+%(indent)s %(text)s
+%(indent)s
+%(indent)s
+%(indent)s '''
+
+_navpoint_indent = ' '
+_navPoint_template = 'navPoint%d'
+
+_content_template = u'''\
+
+
+
+ %(lang)s
+ %(title)s
+ %(author)s
+ %(publisher)s
+ %(copyright)s
+ %(id)s
+
+
+
+%(files)s
+
+
+%(spine)s
+
+
+'''
+
+_file_template = u'''\
+ '''
+
+_spine_template = u'''\
+ '''
+
+_toctree_template = u'toctree-l%d'
+
+_media_types = {
+ '.html': 'application/xhtml+xml',
+ '.css': 'text/css',
+ '.png': 'image/png',
+ '.gif': 'image/gif',
+ '.svg': 'image/svg+xml',
+ '.jpg': 'image/jpeg',
+ '.jpeg': 'image/jpeg',
+ '.otf': 'application/x-font-otf',
+ '.ttf': 'application/x-font-ttf',
+}
+
+
+# The epub publisher
+
+class EpubBuilder(StandaloneHTMLBuilder):
+ """Builder that outputs epub files.
+ It creates the metainfo files
+ container.opf, toc.ncx, mimetype, and META-INF/container.xml.
+ Afterwards, all necessary files are zipped to an epub file.
+ """
+ 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)
+ # the output files for HTML help must be .html only
+ self.out_suffix = '.html'
+ self.playorder = 0
+
+
+ # generic support functions
+ def make_id(self, name):
+ """Replace all characters not allowed for (X)HTML ids"""
+ return name.replace('/', '_')
+
+ def esc(self, name):
+ """Replace all characters not allowed in text an attribute values"""
+ # Like cgi.escape, but also replace apostrophe
+ name = name.replace('&', '&')
+ name = name.replace('<', '<')
+ name = name.replace('>', '>')
+ name = name.replace('"', '"')
+ name = name.replace('\'', ''')
+ return name
+
+ def collapse_text(self, doctree, result):
+ """Remove all HTML markup and return only the text nodes"""
+ for c in doctree.children:
+ if isinstance(c, nodes.Text):
+ result.append(c.data)
+ else:
+ result = self.collapse_text(c, result)
+ return result
+
+ def get_refnodes(self, doctree, result):
+ """Collect section titles, their depth in the toc and the refuri"""
+ # XXX: is there a betterr way than checking the attribute
+ # toctree-l[1-6] on the parent node?
+ if isinstance(doctree, nodes.reference):
+ classes = doctree.parent.attributes['classes']
+ level = 1
+ for l in range(5,0,-1): # or range(1,6)?
+ if (_toctree_template % l) in classes:
+ level = l
+ result.append({
+ 'level': level,
+ 'refuri': self.esc(doctree['refuri']),
+ 'text': self.esc(''.join(self.collapse_text(doctree, [])))
+ })
+ else:
+ for elem in doctree.children:
+ result = self.get_refnodes(elem, result)
+ return result
+
+ def get_toc(self):
+ """Get the total table of contents, containg the master_doc
+ and pre and post files not managed by sphinx"""
+ doctree = self.env.get_and_resolve_doctree(self.config.master_doc, self)
+ self.refnodes = self.get_refnodes(doctree, [])
+ self.refnodes.insert(0, {
+ 'level': 1,
+ 'refuri': self.esc(self.config.master_doc + '.html'),
+ 'text': self.esc(''.join(self.collapse_text(
+ self.env.titles[self.config.master_doc], []
+ ))),
+ })
+ # XXX: is reversed ok?
+ for file, text in reversed(self.config.epub_pre_files):
+ self.refnodes.insert(0, {
+ 'level': 1,
+ 'refuri': self.esc(file + '.html'),
+ 'text': self.esc(text)
+ })
+ for file, text in self.config.epub_post_files:
+ self.refnodes.append({
+ 'level': 1,
+ 'refuri': self.esc(file + '.html'),
+ 'text': self.esc(text)
+ })
+
+
+ # Finish by building the epub file
+ def handle_finish(self):
+ """Create the metainfo files and finally the epub"""
+ self.get_toc()
+ self.build_mimetype(self.outdir, 'mimetype')
+ self.build_container(self.outdir, 'META-INF/container.xml')
+ self.build_content(self.outdir, 'content.opf')
+ self.build_toc(self.outdir, 'toc.ncx')
+ self.build_epub(self.outdir, self.config.epub_basename + '.epub')
+
+ def build_mimetype(self, outdir, outname):
+ """Write the metainfo file mimetype"""
+ self.info('writing %s file...' % outname)
+ f = codecs.open(path.join(outdir, outname), 'w', 'utf-8')
+ try:
+ f.write(_mimetype_template)
+ finally:
+ f.close()
+
+ def build_container(self, outdir, outname):
+ """Write the metainfo file META-INF/cointainer.xml"""
+ self.info('writing %s file...' % outname)
+ fn = path.join(outdir, outname)
+ try:
+ os.mkdir(path.dirname(fn))
+ except OSError, err:
+ if err.errno != os.errno.EEXIST:
+ raise
+ f = codecs.open(path.join(outdir, outname), 'w', 'utf-8')
+ try:
+ f.write(_container_template)
+ finally:
+ f.close()
+
+ def content_metadata(self, files, spine):
+ """Create a dictionary with all metadata for the content.opf
+ file properly escaped"""
+ metadata = {}
+ metadata['title'] = self.esc(self.config.epub_title)
+ metadata['author'] = self.esc(self.config.epub_author)
+ metadata['uid'] = self.esc(self.config.epub_uid)
+ metadata['lang'] = self.esc(self.config.epub_language)
+ metadata['publisher'] = self.esc(self.config.epub_publisher)
+ metadata['copyright'] = self.esc(self.config.epub_copyright)
+ metadata['scheme'] = self.esc(self.config.epub_scheme)
+ metadata['id'] = self.esc(self.config.epub_identifier)
+ metadata['files'] = files
+ metadata['spine'] = spine
+ return metadata
+
+ def build_content(self, outdir, outname):
+ """Write the metainfo file content.opf
+ It contains bibliographic data, a file list and
+ the spine (the reading order)."""
+ self.info('writing %s file...' % outname)
+
+ # files
+ if not outdir.endswith(os.sep):
+ outdir += os.sep
+ olen = len(outdir)
+ projectfiles = []
+ self.files = []
+ self.ignored_files = ['.buildinfo',
+ 'mimetype', 'content.opf', 'toc.ncx', 'META-INF/container.xml',
+ self.config.epub_basename + '.epub'] + \
+ self.config.epub_exclude_files
+ for root, dirs, files in os.walk(outdir):
+ for fn in files:
+ filename = path.join(root, fn)[olen:]
+ if filename in self.ignored_files:
+ # self.warn("ignoring %s" % filename)
+ continue
+ ext = path.splitext(filename)[-1]
+ if ext not in _media_types:
+ self.warn("unknown mimetype for %s, ignoring" % filename)
+ continue
+ projectfiles.append(_file_template % {
+ 'href': self.esc(filename),
+ 'id': self.esc(self.make_id(filename)),
+ 'media_type': self.esc(_media_types[ext])
+ })
+ self.files.append(filename)
+ projectfiles = '\n'.join(projectfiles)
+
+ # spine
+ spine = []
+ for item in self.refnodes:
+ if '#' in item['refuri']:
+ continue
+ if item['refuri'] in self.ignored_files:
+ continue
+ spine.append(_spine_template % {
+ 'idref': self.esc(self.make_id(item['refuri']))
+ })
+ spine = '\n'.join(spine)
+
+ # write the project file
+ f = codecs.open(path.join(outdir, outname), 'w', 'utf-8')
+ try:
+ f.write(_content_template % \
+ self.content_metadata(projectfiles, spine))
+ finally:
+ f.close()
+
+ def new_navpoint(self, node, level, incr=True):
+ """Create a new entry in the toc from the node at given level"""
+ # XXX Modifies the node
+ if incr:
+ self.playorder += 1
+ node['indent'] = _navpoint_indent * level
+ node['navpoint'] = self.esc(_navPoint_template % self.playorder)
+ node['playorder'] = self.playorder
+ return _navpoint_template % node
+
+ def insert_subnav(self, node, subnav):
+ """Insert nested navpoints for given node
+ The node and subnav are already rendered to text"""
+ nlist = node.split('\n')
+ nlist.insert(-1, subnav)
+ return '\n'.join(nlist)
+
+ def build_navpoints(self, nodes):
+ """Create the toc navigation structure
+ Subelements of a node are nested inside the navpoint.
+ For nested nodes the parent node is reinserted in the subnav."""
+ navstack = []
+ navlist = []
+ level = 1
+ lastnode = None
+ for node in nodes:
+ file = node['refuri'].split('#')[0]
+ if file in self.ignored_files:
+ continue
+ if node['level'] == level:
+ navlist.append(self.new_navpoint(node,level))
+ elif node['level'] == level + 1:
+ navstack.append(navlist)
+ navlist = []
+ level += 1
+ if lastnode:
+ # Insert starting point in subtoc with same playOrder
+ navlist.append(self.new_navpoint(lastnode, level, False))
+ navlist.append(self.new_navpoint(node, level))
+ else:
+ while node['level'] < level:
+ subnav = '\n'.join(navlist)
+ navlist = navstack.pop()
+ navlist[-1] = self.insert_subnav(navlist[-1], subnav)
+ level -= 1
+ navlist.append(self.new_navpoint(node, level))
+ lastnode = node
+ while level != 1:
+ subnav = '\n'.join(navlist)
+ navlist = navstack.pop()
+ navlist[-1] = self.insert_subnav(navlist[-1], subnav)
+ level -= 1
+ return '\n'.join(navlist)
+
+ def toc_metadata(self, level, navpoints):
+ """Create a dictionary with all metadata for the toc.ncx
+ file properly escaped"""
+ metadata = {}
+ metadata['uid'] = self.config.epub_uid
+ metadata['title'] = self.config.epub_title
+ metadata['level'] = level
+ metadata['navpoints'] = navpoints
+ return metadata
+
+ def build_toc(self, outdir, outname):
+ """Write the metainfo file toc.ncx"""
+ self.info('writing %s file...' % outname)
+
+ navpoints = self.build_navpoints(self.refnodes)
+ level = max(item['level'] for item in self.refnodes)
+ f = codecs.open(path.join(outdir, outname), 'w', 'utf-8')
+ try:
+ f.write(_toc_template % self.toc_metadata(level, navpoints))
+ finally:
+ f.close()
+
+ def build_epub(self, outdir, outname):
+ """Write the epub file
+ It is a zip file with the mimetype file stored uncompressed
+ as the first entry."""
+ self.info('writing %s file...' % outname)
+ projectfiles = ['META-INF/container.xml', 'content.opf', 'toc.ncx'] \
+ + self.files
+ epub = zipfile.ZipFile(path.join(outdir, outname), 'w', \
+ zipfile.ZIP_DEFLATED)
+ epub.write(path.join(outdir, 'mimetype'), 'mimetype', \
+ zipfile.ZIP_STORED)
+ for file in projectfiles:
+ epub.write(path.join(outdir, file), file, zipfile.ZIP_DEFLATED)
+ epub.close()
+
diff --git a/sphinx/config.py b/sphinx/config.py
index b2ef29fb1..5050a7796 100644
--- a/sphinx/config.py
+++ b/sphinx/config.py
@@ -112,6 +112,20 @@ class Config(object):
latex_docclass = ({}, None),
# now deprecated - use latex_elements
latex_preamble = ('', None),
+
+ # Epub options
+ epub_basename = (lambda self: make_filename(self.project), None),
+ epub_title = (lambda self: self.html_title, 'html'),
+ epub_author = ('unknown', 'html'),
+ epub_language = (lambda self: self.language or 'en', 'html'),
+ epub_publisher = ('unknown', 'html'),
+ epub_copyright = (lambda self: self.copyright, 'html'),
+ epub_identifier = ('unknown', 'html'),
+ epub_scheme = ('unknown', 'html'),
+ epub_uid = ('unknown', 'env'),
+ epub_pre_files = ([], 'env'),
+ epub_post_files = ([], 'env'),
+ epub_exclude_files = ([], 'env'),
)
def __init__(self, dirname, filename, overrides, tags):
diff --git a/sphinx/quickstart.py b/sphinx/quickstart.py
index 7b11dfdd4..eaf9f55a0 100644
--- a/sphinx/quickstart.py
+++ b/sphinx/quickstart.py
@@ -223,6 +223,56 @@ latex_documents = [
# If false, no module index is generated.
#latex_use_modindex = True
+
+
+# -- Options for Epub output ---------------------------------------------------
+
+# Please also set the html_theme to 'epub' or any other approriate theme.
+# The display size is quite small for ebook readers.
+# The default themes may take too much space.
+
+# bibliographic Dublin Core description of the content.opf and
+# in the toc.ncx file. It defaults to the html_title option.
+#epub_title = ''
+
+# The author of the text. The author is inserted into the
+# bibliographic Dublin Core description of the content.opf file.
+#epub_author = ''
+
+# The language of the text. It defaults to the language option
+# or en if the language is not set.
+#epub_language = ''
+
+# The publisher of the text. The publisher is inserted into the
+# bibliographic Dublin Core description of the content.opf file.
+# You may use the project homepage.
+#epub_publisher = ''
+
+# The copyright of the text. The copyright is inserted into the
+# bibliographci Dublin Core description of the content.opf file.
+# It defaults to the copyright option.
+#epub_copyright = ''
+
+# The unique identifier of the text. This can be a ISBN number
+# or the project homepage.
+#epub_identifier = ''
+
+# The scheme of the identifier. Typical schemes are ISBN or URL.
+#epub_scheme = ''
+
+# A unique identification for the text.
+#epub_uid = ''
+
+# HTML files that should be inserted before the pages created by sphinx.
+# The format is a list of tuples containing the path and title.
+#epub_pre_files = []
+
+# HTML files shat should be inserted after the pages created by sphinx.
+# The format is a list of tuples containing the path and title.
+#epub_post_files = []
+
+# A list of files that should not be packed into the epub file.
+#epub_exclude_files = []
'''
INTERSPHINX_CONFIG = '''
@@ -270,8 +320,8 @@ PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) \
$(SPHINXOPTS) %(rsrcdir)s
-.PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes \
-linkcheck doctest
+.PHONY: help clean html dirhtml pickle json htmlhelp qthelp epub \
+latex changes linkcheck doctest
help:
\t@echo "Please use \\`make ' where is one of"
@@ -282,6 +332,7 @@ help:
\t@echo " htmlhelp to make HTML files and a HTML help project"
\t@echo " qthelp to make HTML files and a qthelp project"
\t@echo " devhelp to make HTML files and a Devhelp project"
+\t@echo " epub to make an epub"
\t@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
\t@echo " latexpdf to make LaTeX files and run them through pdflatex"
\t@echo " changes to make an overview of all changed/added/deprecated items"
@@ -337,6 +388,11 @@ devhelp:
$$HOME/.local/share/devhelp/%(project_fn)s"
\t@echo "# devhelp"
+epub:
+\t$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
+\t@echo
+\t@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
+
latex:
\t$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
\t@echo
@@ -391,6 +447,7 @@ if "%%1" == "help" (
\techo. htmlhelp to make HTML files and a HTML help project
\techo. qthelp to make HTML files and a qthelp project
\techo. devhelp to make HTML files and a Devhelp project
+\techo. epub to make an epub
\techo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
\techo. changes to make an overview over all changed/added/deprecated items
\techo. linkcheck to check all external links for integrity
@@ -458,6 +515,13 @@ if "%%1" == "devhelp" (
\tgoto end
)
+if "%%1" == "epub" (
+\t%%SPHINXBUILD%% -b epub %%ALLSPHINXOPTS%% %%BUILDDIR%%/epub
+\techo.
+\techo.Build finished. The epub file is in %%BUILDDIR%%/epub.
+\tgoto end
+)
+
if "%%1" == "latex" (
\t%%SPHINXBUILD%% -b latex %%ALLSPHINXOPTS%% %%BUILDDIR%%/latex
\techo.
diff --git a/sphinx/themes/epub/layout.html b/sphinx/themes/epub/layout.html
new file mode 100644
index 000000000..64b1a4cb2
--- /dev/null
+++ b/sphinx/themes/epub/layout.html
@@ -0,0 +1,7 @@
+{% extends "basic/layout.html" %}
+
+{# add only basic navigation links #}
+{% block sidebar1 %}{% endblock %}
+{% block sidebar2 %}{% endblock %}
+{% block relbar2 %}{% endblock %}
+{% block linktags %}{% endblock %}
diff --git a/sphinx/themes/epub/static/epub.css b/sphinx/themes/epub/static/epub.css
new file mode 100644
index 000000000..72b771905
--- /dev/null
+++ b/sphinx/themes/epub/static/epub.css
@@ -0,0 +1,445 @@
+/**
+ * Sphinx stylesheet -- epub theme
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+/* -- main layout ----------------------------------------------------------- */
+
+div.clearer {
+ clear: both;
+}
+
+a:link, a:visited {
+ color: #3333ff;
+ text-decoration: underline;
+}
+
+img {
+ border: 0;
+}
+
+/* -- relbar ---------------------------------------------------------------- */
+
+div.related {
+ width: 100%;
+ font-family: sans-serif;
+ font-size: 90%;
+}
+
+div.related h3 {
+ display: none;
+}
+
+div.related ul {
+ margin: 0;
+ padding: 0 0 0 10px;
+ list-style: none;
+}
+
+div.related li {
+ display: inline;
+}
+
+div.related li.right {
+ float: right;
+ margin-right: 5px;
+}
+
+/* -- sidebar --------------------------------------------------------------- */
+
+div.sphinxsidebarwrapper {
+ padding: 10px 5px 0 10px;
+}
+
+div.sphinxsidebar {
+ float: left;
+ width: 230px;
+ margin-left: -100%;
+ font-size: 90%;
+}
+
+div.sphinxsidebar ul {
+ list-style: none;
+}
+
+div.sphinxsidebar ul ul,
+div.sphinxsidebar ul.want-points {
+ margin-left: 20px;
+ list-style: square;
+}
+
+div.sphinxsidebar ul ul {
+ margin-top: 0;
+ margin-bottom: 0;
+}
+
+div.sphinxsidebar form {
+ margin-top: 10px;
+}
+
+div.sphinxsidebar input {
+ border: 1px solid #98dbcc;
+ font-family: sans-serif;
+ font-size: 100%;
+}
+
+img {
+ border: 0;
+}
+
+/* -- search page ----------------------------------------------------------- */
+
+ul.search {
+ margin: 10px 0 0 20px;
+ padding: 0;
+}
+
+ul.search li {
+ padding: 5px 0 5px 20px;
+ background-image: url(file.png);
+ background-repeat: no-repeat;
+ background-position: 0 7px;
+}
+
+ul.search li a {
+ font-weight: bold;
+}
+
+ul.search li div.context {
+ color: #888;
+ margin: 2px 0 0 30px;
+ text-align: left;
+}
+
+ul.keywordmatches li.goodmatch a {
+ font-weight: bold;
+}
+
+/* -- index page ------------------------------------------------------------ */
+
+table.contentstable {
+ width: 90%;
+}
+
+table.contentstable p.biglink {
+ line-height: 150%;
+}
+
+a.biglink {
+ font-size: 130%;
+}
+
+span.linkdescr {
+ font-style: italic;
+ padding-top: 5px;
+ font-size: 90%;
+}
+
+/* -- general index --------------------------------------------------------- */
+
+table.indextable td {
+ text-align: left;
+ vertical-align: top;
+}
+
+table.indextable dl, table.indextable dd {
+ margin-top: 0;
+ margin-bottom: 0;
+}
+
+table.indextable tr.pcap {
+ height: 10px;
+}
+
+table.indextable tr.cap {
+ margin-top: 10px;
+ background-color: #f2f2f2;
+}
+
+img.toggler {
+ margin-right: 3px;
+ margin-top: 3px;
+ cursor: pointer;
+}
+
+/* -- general body styles --------------------------------------------------- */
+
+a.headerlink {
+ visibility: hidden;
+}
+
+div.body p.caption {
+ text-align: inherit;
+}
+
+div.body td {
+ text-align: left;
+}
+
+.field-list ul {
+ padding-left: 100%;
+}
+
+.first {
+ margin-top: 0 !important;
+}
+
+p.rubric {
+ margin-top: 30px;
+ font-weight: bold;
+}
+
+/* -- sidebars -------------------------------------------------------------- */
+
+div.sidebar {
+ margin: 0 0 0.5em 1em;
+ border: 1px solid #ddb;
+ padding: 7px 7px 0 7px;
+ background-color: #ffe;
+ width: 40%;
+ float: right;
+}
+
+p.sidebar-title {
+ font-weight: bold;
+}
+
+/* -- topics ---------------------------------------------------------------- */
+
+div.topic {
+ border: 1px solid #ccc;
+ padding: 7px 7px 0 7px;
+ margin: 10px 0 10px 0;
+}
+
+p.topic-title {
+ font-size: 110%;
+ font-weight: bold;
+ margin-top: 10px;
+}
+
+/* -- admonitions ----------------------------------------------------------- */
+
+div.admonition {
+ margin-top: 10px;
+ margin-bottom: 10px;
+ padding: 7px;
+}
+
+div.admonition dt {
+ font-weight: bold;
+}
+
+div.admonition dl {
+ margin-bottom: 0;
+}
+
+p.admonition-title {
+ margin: 0px 10px 5px 0px;
+ font-weight: bold;
+}
+
+div.body p.centered {
+ text-align: center;
+ margin-top: 25px;
+}
+
+/* -- tables ---------------------------------------------------------------- */
+
+table.docutils {
+ border: 0;
+ border-collapse: collapse;
+}
+
+table.docutils td, table.docutils th {
+ padding: 1px 8px 1px 5px;
+ border-top: 0;
+ border-left: 0;
+ border-right: 0;
+ border-bottom: 1px solid #aaa;
+}
+
+table.field-list td, table.field-list th {
+ border: 0 !important;
+}
+
+table.footnote td, table.footnote th {
+ border: 0 !important;
+}
+
+th {
+ text-align: left;
+ padding-right: 5px;
+}
+
+/* -- other body styles ----------------------------------------------------- */
+
+dl {
+ margin-bottom: 15px;
+}
+
+dd p {
+ margin-top: 0px;
+}
+
+dd ul, dd table {
+ margin-bottom: 10px;
+}
+
+dd {
+ margin-top: 3px;
+ margin-bottom: 10px;
+ margin-left: 30px;
+}
+
+dt:target, .highlight {
+ background-color: #ddd;
+}
+
+dl.glossary dt {
+ font-weight: bold;
+ font-size: 110%;
+}
+
+.field-list ul {
+ margin: 0;
+ padding-left: 1em;
+}
+
+.field-list p {
+ margin: 0;
+}
+
+.refcount {
+ color: #060;
+}
+
+.optional {
+ font-size: 130%;
+}
+
+.versionmodified {
+ font-style: italic;
+}
+
+.system-message {
+ background-color: #fda;
+ padding: 5px;
+ border: 3px solid red;
+}
+
+.footnote:target {
+ background-color: #dddddd;
+}
+
+/* -- code displays --------------------------------------------------------- */
+
+pre {
+ font-family: "LiberationNarrow", monospace;
+ overflow: auto;
+}
+
+td.linenos pre {
+ padding: 5px 0px;
+ border: 0;
+ background-color: transparent;
+ color: #aaa;
+}
+
+table.highlighttable {
+ margin-left: 0.5em;
+}
+
+table.highlighttable td {
+ padding: 0 0.5em 0 0.5em;
+}
+
+tt {
+ font-family: "LiberationNarrow", monospace;
+}
+
+tt.descname {
+ background-color: transparent;
+ font-weight: bold;
+ font-size: 1.2em;
+}
+
+tt.descclassname {
+ background-color: transparent;
+}
+
+tt.xref, a tt {
+ background-color: transparent;
+ font-weight: bold;
+}
+
+h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt {
+ background-color: transparent;
+}
+
+/* -- math display ---------------------------------------------------------- */
+
+img.math {
+ vertical-align: middle;
+}
+
+div.body div.math p {
+ text-align: center;
+}
+
+span.eqno {
+ float: right;
+}
+
+/* -- special divs --------------------------------------------------------- */
+
+div.quotebar {
+ background-color: #e3eff1;
+ max-width: 250px;
+ float: right;
+ font-family: sans-serif;
+ padding: 7px 7px;
+ border: 1px solid #ccc;
+}
+div.footer {
+ background-color: #e3eff1;
+ padding: 3px 8px 3px 0;
+ clear: both;
+ font-family: sans-serif;
+ font-size: 80%;
+ text-align: right;
+}
+
+div.footer a {
+ text-decoration: underline;
+}
+
+@font-face {
+ font-family: "LiberationNarrow";
+ font-style: normal;
+ font-weight: normal;
+ src: url("res:///Data/fonts/LiberationNarrow-Regular.otf")
+ format("opentype");
+}
+@font-face {
+ font-family: "LiberationNarrow";
+ font-style: oblique, italic;
+ font-weight: normal;
+ src: url("res:///Data/fonts/LiberationNarrow-Italic.otf")
+ format("opentype");
+}
+@font-face {
+ font-family: "LiberationNarrow";
+ font-style: normal;
+ font-weight: bold;
+ src: url("res:///Data/fonts/LiberationNarrow-Bold.otf")
+ format("opentype");
+}
+@font-face {
+ font-family: "LiberationNarrow";
+ font-style: oblique, italic;
+ font-weight: bold;
+ src: url("res:///Data/fonts/LiberationNarrow-BoldItalic.otf")
+ format("opentype");
+}
+
diff --git a/sphinx/themes/epub/theme.conf b/sphinx/themes/epub/theme.conf
new file mode 100644
index 000000000..d5806ec50
--- /dev/null
+++ b/sphinx/themes/epub/theme.conf
@@ -0,0 +1,4 @@
+[theme]
+inherit = basic
+stylesheet = epub.css
+pygments_style = none
diff --git a/tests/test_build.py b/tests/test_build.py
index 47311f276..d7b194742 100644
--- a/tests/test_build.py
+++ b/tests/test_build.py
@@ -279,6 +279,10 @@ def test_htmlhelp(app):
def test_qthelp(app):
app.builder.build_all()
+@with_app(buildername='epub')
+def test_epub(app):
+ app.builder.build_all()
+
@with_app(buildername='changes', cleanenv=True)
def test_changes(app):
app.builder.build_all()
diff --git a/tests/test_theming.py b/tests/test_theming.py
index 065468b07..cdaf2baff 100644
--- a/tests/test_theming.py
+++ b/tests/test_theming.py
@@ -25,7 +25,7 @@ def test_theme_api(app):
# test Theme class API
assert set(Theme.themes.keys()) == \
set(['basic', 'default', 'scrolls', 'agogo', 'sphinxdoc',
- 'traditional', 'testtheme', 'ziptheme'])
+ 'traditional', 'testtheme', 'ziptheme', 'epub'])
assert Theme.themes['testtheme'][1] is None
assert isinstance(Theme.themes['ziptheme'][1], zipfile.ZipFile)