diff --git a/AUTHORS b/AUTHORS
index 09be75330..ebda43077 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -15,6 +15,7 @@ Other contributors, listed alphabetically, are:
* Thomas Lamb -- linkcheck builder
* Dan MacKinlay -- metadata fixes
* Will Maier -- directory HTML builder
+* Roland Meister -- epub builder
* Christopher Perkins -- autosummary integration
* Benjamin Peterson -- unittests
* Stefan Seefeld -- toctree improvements
diff --git a/CHANGES b/CHANGES
index 9312382b2..046da6620 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,8 +1,14 @@
Release 1.0 (in development)
============================
+.. XXX add short info about domains
+
+* Support for domains has been added.
+
* Support for docutils 0.4 has been removed.
+* Added Epub builder.
+
* #284: All docinfo metadata is now put into the document metadata, not
just the author.
@@ -54,6 +60,12 @@ Release 1.0 (in development)
Release 0.6.4 (in development)
==============================
+* #303: ``html_context`` values given on the command line via ``-A``
+ should not override other values given in conf.py.
+
+* Fix a bug preventing incremental rebuilds for the ``dirhtml``
+ builder.
+
* #299: Fix the mangling of quotes in some literal blocks.
* #292: Fix path to the search index for the ``dirhtml`` builder.
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..60d217df5 100644
--- a/doc/_templates/layout.html
+++ b/doc/_templates/layout.html
@@ -1,10 +1,13 @@
{% extends "!layout.html" %}
{% block extrahead %}
+{{ super() }}
+{%- if not embedded %}
+{%- endif %}
{% endblock %}
{% block rootrellink %}
diff --git a/doc/builders.rst b/doc/builders.rst
index 7c1f03d3d..e8dccc430 100644
--- a/doc/builders.rst
+++ b/doc/builders.rst
@@ -63,6 +63,16 @@ 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. See :ref:`epub-faq` for
+ details about it. For definition of the epub format, have a look at
+ ``_ or ``_.
+
+ Its name is ``epub``.
+
.. module:: sphinx.builders.latex
.. class:: LaTeXBuilder
diff --git a/doc/conf.py b/doc/conf.py
index e8db04500..9cd3803ae 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -64,6 +64,19 @@ html_use_opensearch = 'http://sphinx.pocoo.org'
# Output file base name for HTML help builder.
htmlhelp_basename = 'Sphinxdoc'
+# Epub fields
+epub_theme = 'epub'
+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 529f9cf8d..daceaf789 100644
--- a/doc/config.rst
+++ b/doc/config.rst
@@ -550,6 +550,96 @@ 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 builder derives from the HTML
+builder, 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_theme
+
+ The HTML theme for the epub output. Since the default themes are not
+ optimized for small screen space, using the same theme for HTML and epub
+ output is usually not wise. This defaults to ``'epub'``, a theme designed to
+ save visual space.
+
+.. 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. The default value
+ is ``'unknown'``.
+
+.. 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 d429ba6fe..05b39ef4d 100644
--- a/doc/faq.rst
+++ b/doc/faq.rst
@@ -66,3 +66,40 @@ github pages
.. _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
+
+
+.. _epub-faq:
+
+Epub info
+---------
+
+The epub builder 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_, Calibre_, FBreader_ (although it does not render the CSS),
+ and Bookworm_. For bookworm you can download the source from
+ http://code.google.com/p/threepress/ and run your own local server.
+
+.. _Epubcheck: http://code.google.com/p/epubcheck/
+.. _Calibre: http://calibre-ebook.com/
+.. _FBreader: http://www.fbreader.org/
+.. _Bookworm: http://bookworm.oreilly.com/
diff --git a/doc/theming.rst b/doc/theming.rst
index 7af54a0be..c73b4f45d 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 builder. There are currently no options.
+ This theme tries to save 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 08628c7b6..d02b58acc 100644
--- a/sphinx/builders/__init__.py
+++ b/sphinx/builders/__init__.py
@@ -323,6 +323,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..7a11b291d
--- /dev/null
+++ b/sphinx/builders/epub.py
@@ -0,0 +1,421 @@
+# -*- 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 codecs
+from os import path
+import zipfile
+
+from docutils import nodes
+
+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 epub must be .html only
+ self.out_suffix = '.html'
+ self.playorder = 0
+
+ def get_theme_config(self):
+ return self.config.epub_theme, {}
+
+ # 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):
+ try:
+ # docutils 0.4 and 0.5: Text is a UserString subclass
+ result.append(c.data)
+ except AttributeError:
+ # docutils 0.6: Text is a unicode subclass
+ result.append(c)
+ 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 better 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/builders/html.py b/sphinx/builders/html.py
index 641db9c02..e5b0adb48 100644
--- a/sphinx/builders/html.py
+++ b/sphinx/builders/html.py
@@ -103,9 +103,14 @@ class StandaloneHTMLBuilder(Builder):
if path.isfile(jsfile):
self.script_files.append('_static/translations.js')
+ def get_theme_config(self):
+ return self.config.html_theme, self.config.html_theme_options
+
def init_templates(self):
Theme.init_themes(self)
- self.theme = Theme(self.config.html_theme)
+ themename, themeoptions = self.get_theme_config()
+ self.theme = Theme(themename)
+ self.theme_options = themeoptions.copy()
self.create_template_bridge()
self.templates.init(self, self.theme)
@@ -169,8 +174,7 @@ class StandaloneHTMLBuilder(Builder):
if docname not in self.env.all_docs:
yield docname
continue
- targetname = self.env.doc2path(docname, self.outdir,
- self.out_suffix)
+ targetname = self.get_outfilename(docname)
try:
targetmtime = path.getmtime(targetname)
except Exception:
@@ -283,8 +287,7 @@ class StandaloneHTMLBuilder(Builder):
if self.theme:
self.globalcontext.update(
('theme_' + key, val) for (key, val) in
- self.theme.get_options(
- self.config.html_theme_options).iteritems())
+ self.theme.get_options(self.theme_options).iteritems())
self.globalcontext.update(self.config.html_context)
def get_doc_context(self, docname, body, metatags):
diff --git a/sphinx/cmdline.py b/sphinx/cmdline.py
index 898678b72..ae8b076b2 100644
--- a/sphinx/cmdline.py
+++ b/sphinx/cmdline.py
@@ -96,7 +96,6 @@ def main(argv):
error = sys.stderr
warnfile = None
confoverrides = {}
- htmlcontext = {}
tags = []
doctreedir = path.join(outdir, '.doctrees')
for opt, val in opts:
@@ -142,7 +141,7 @@ def main(argv):
val = int(val)
except ValueError:
pass
- htmlcontext[key] = val
+ confoverrides['html_context.%s' % key] = val
elif opt == '-N':
nocolor()
elif opt == '-E':
@@ -158,7 +157,6 @@ def main(argv):
warnfile = val
elif opt == '-P':
use_pdb = True
- confoverrides['html_context'] = htmlcontext
if warning and warnfile:
warnfp = open(warnfile, 'w')
diff --git a/sphinx/config.py b/sphinx/config.py
index dbe569fb7..5bdf817d0 100644
--- a/sphinx/config.py
+++ b/sphinx/config.py
@@ -98,6 +98,21 @@ class Config(object):
# Devhelp only options
devhelp_basename = (lambda self: make_filename(self.project), None),
+ # Epub options
+ epub_basename = (lambda self: make_filename(self.project), None),
+ epub_theme = ('epub', 'html'),
+ 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'),
+
# LaTeX options
latex_documents = ([], None),
latex_logo = (None, None),
diff --git a/sphinx/directives/other.py b/sphinx/directives/other.py
index 4436b306e..01bd74ce1 100644
--- a/sphinx/directives/other.py
+++ b/sphinx/directives/other.py
@@ -148,7 +148,7 @@ class Index(Directive):
option_spec = {}
indextypes = [
- 'single', 'pair', 'triple',
+ 'single', 'pair', 'double', 'triple',
]
def run(self):
@@ -171,6 +171,8 @@ class Index(Directive):
for type in self.indextypes:
if entry.startswith(type+':'):
value = entry[len(type)+1:].strip()
+ if type == 'double':
+ type = 'pair'
ne.append((type, value, targetid, value))
break
# shorthand notation for single entries
diff --git a/sphinx/quickstart.py b/sphinx/quickstart.py
index 7b11dfdd4..e09bee33f 100644
--- a/sphinx/quickstart.py
+++ b/sphinx/quickstart.py
@@ -223,6 +223,40 @@ latex_documents = [
# If false, no module index is generated.
#latex_use_modindex = True
+
+
+# -- Options for Epub output ---------------------------------------------------
+
+# Bibliographic Dublin Core info.
+#epub_title = ''
+#epub_author = ''
+#epub_publisher = ''
+#epub_copyright = ''
+
+# The language of the text. It defaults to the language option
+# or en if the language is not set.
+#epub_language = ''
+
+# The scheme of the identifier. Typical schemes are ISBN or URL.
+#epub_scheme = ''
+
+# The unique identifier of the text. This can be a ISBN number
+# or the project homepage.
+#epub_identifier = ''
+
+# 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 +304,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 +316,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 +372,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 +431,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 +499,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/root/_templates/layout.html b/tests/root/_templates/layout.html
index d312238fa..82125df8e 100644
--- a/tests/root/_templates/layout.html
+++ b/tests/root/_templates/layout.html
@@ -1,7 +1,10 @@
{% extends "!layout.html" %}
{% block extrahead %}
+{# html_context variable from conf.py #}
+{# html_context variable from confoverrides (as if given on cmdline) #}
+
{{ super() }}
{% endblock %}
diff --git a/tests/root/conf.py b/tests/root/conf.py
index 13b267994..e1ebc489c 100644
--- a/tests/root/conf.py
+++ b/tests/root/conf.py
@@ -36,7 +36,7 @@ html_theme_options = {'testopt': 'testoverride'}
html_style = 'default.css'
html_static_path = ['_static']
html_last_updated_fmt = '%b %d, %Y'
-html_context = {'hckey': 'hcval'}
+html_context = {'hckey': 'hcval', 'hckey_co': 'wrong_hcval_co'}
htmlhelp_basename = 'SphinxTestsdoc'
diff --git a/tests/root/markup.txt b/tests/root/markup.txt
index c5022b32f..65156e7e6 100644
--- a/tests/root/markup.txt
+++ b/tests/root/markup.txt
@@ -227,6 +227,7 @@ Index markup
.. index::
single: entry
pair: entry; pair
+ double: entry; double
triple: index; entry; triple
keyword: with
diff --git a/tests/test_build.py b/tests/test_build.py
index 9d3210e75..bceba3c0f 100644
--- a/tests/test_build.py
+++ b/tests/test_build.py
@@ -124,6 +124,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_build_html.py b/tests/test_build_html.py
index 7079971c4..ebd38cb2d 100644
--- a/tests/test_build_html.py
+++ b/tests/test_build_html.py
@@ -152,6 +152,7 @@ HTML_XPATH = {
},
'contents.html': {
".//meta[@name='hc'][@content='hcval']": '',
+ ".//meta[@name='hc_co'][@content='hcval_co']": '',
".//meta[@name='testopt'][@content='testoverride']": '',
#".//td[@class='label']": r'\[Ref1\]', # docutils 0.5 only
".//td[@class='label']": '',
@@ -228,6 +229,7 @@ def check_xpath(etree, fname, path, check):
[node.text for node in nodes]))
@gen_with_app(buildername='html', warning=html_warnfile, cleanenv=True,
+ confoverrides={'html_context.hckey_co': 'hcval_co'},
tags=['testtag'])
def test_html(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)