Review of epub builder. Add a separate "epub_theme" config value for selecting a different theme.

This commit is contained in:
Georg Brandl 2009-12-29 12:32:42 +01:00
parent b6f9399961
commit 24fb46226f
10 changed files with 148 additions and 161 deletions

View File

@ -1,12 +1,13 @@
{% extends "!layout.html" %}
{% block extrahead %}
{{ super() }}
{%- if not embedded %}
<style type="text/css">
table.right { float: right; margin-left: 20px; }
table.right td { border: 1px solid #ccc; }
</style>
{% endif %}
{%- endif %}
{% endblock %}
{% block rootrellink %}

View File

@ -67,11 +67,9 @@ The builder's "name" must be given to the **-b** command-line option of
.. 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 `<http://www.idpf.org/specs.htm>`_ or
`<http://en.wikipedia.org/wiki/EPUB>`_ for the definition of epubs.
also generates an *epub* file for ebook readers. See
`<http://www.idpf.org/specs.htm>`_ or `<http://en.wikipedia.org/wiki/EPUB>`_
for the definition of the epub format.
Its name is ``epub``.

View File

@ -34,7 +34,7 @@ release = version
show_authors = True
# The HTML template theme.
html_theme = 'epub'
html_theme = 'sphinxdoc'
# A list of ignored prefixes names for module index sorting.
modindex_common_prefix = ['sphinx.']
@ -65,6 +65,7 @@ html_use_opensearch = 'http://sphinx.pocoo.org'
htmlhelp_basename = 'Sphinxdoc'
# Epub fields
epub_theme = 'epub'
epub_basename = 'sphinx'
epub_author = 'Georg Brandl'
epub_publisher = 'http://sphinx.pocoo.org/'

View File

@ -554,68 +554,66 @@ that use Sphinx' HTMLWriter class.
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
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 <http://dublincore.org/>`_.
.. 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
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'``.
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.
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'``.
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.
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'``.
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 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'``.
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
@ -631,15 +629,14 @@ the `Dublin Core metadata <http://dublincore.org/>`_.
.. 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 ``[]``.
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 ``[]``.
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:

View File

@ -54,46 +54,40 @@ github pages
Sphinx HTML output.
.. _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
----
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:
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.
* 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 <http://fontforge.sourceforge.net/>`_
and create narrow variants
of some free font. In my case I get up to 70 characters on a line.
* 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 <http://fontforge.sourceforge.net/>`_ 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/
<http://code.google.com/p/epubcheck/>`_),
Calibre
(`http://calibre-ebook.com/ <http://calibre-ebook.com/>`_),
FBreader (`http://www.fbreader.org/ <http://www.fbreader.org/>`_,
although it does not render the CSS), and
Bookworm (`http://bookworm.oreilly.com/ <http://bookworm.oreilly.com/>`_).
For bookworm you can download the source from
`http://code.google.com/p/threepress/ <http://code.google.com/p/threepress/>`_
and run you own local server.
* 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.
.. _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
.. _Epubcheck: http://code.google.com/p/epubcheck/
.. _Calibre: http://calibre-ebook.com/
.. _FBreader: http://www.fbreader.org/
.. _Bookworm: http://bookworm.oreilly.com/

View File

@ -160,9 +160,9 @@ 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.
* **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

View File

@ -11,20 +11,17 @@
"""
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.
# (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!
@ -118,9 +115,10 @@ _media_types = {
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.
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'
@ -136,18 +134,20 @@ class EpubBuilder(StandaloneHTMLBuilder):
def init(self):
StandaloneHTMLBuilder.init(self)
# the output files for HTML help must be .html only
# 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"""
"""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"""
"""Replace all characters not allowed in text an attribute values."""
# Like cgi.escape, but also replace apostrophe
name = name.replace('&', '&amp;')
name = name.replace('<', '&lt;')
@ -157,17 +157,17 @@ class EpubBuilder(StandaloneHTMLBuilder):
return name
def collapse_text(self, doctree, result):
"""Remove all HTML markup and return only the text nodes"""
"""Remove all HTML markup and return only the text nodes."""
for c in doctree.children:
if isinstance(c, nodes.Text):
result.append(c.data)
result.append(unicode(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 betterr way than checking the attribute
"""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']
@ -187,7 +187,8 @@ class EpubBuilder(StandaloneHTMLBuilder):
def get_toc(self):
"""Get the total table of contents, containg the master_doc
and pre and post files not managed by sphinx"""
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, {
@ -214,7 +215,7 @@ class EpubBuilder(StandaloneHTMLBuilder):
# Finish by building the epub file
def handle_finish(self):
"""Create the metainfo files and finally the epub"""
"""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')
@ -223,7 +224,7 @@ class EpubBuilder(StandaloneHTMLBuilder):
self.build_epub(self.outdir, self.config.epub_basename + '.epub')
def build_mimetype(self, outdir, outname):
"""Write the metainfo file mimetype"""
"""Write the metainfo file mimetype."""
self.info('writing %s file...' % outname)
f = codecs.open(path.join(outdir, outname), 'w', 'utf-8')
try:
@ -232,7 +233,7 @@ class EpubBuilder(StandaloneHTMLBuilder):
f.close()
def build_container(self, outdir, outname):
"""Write the metainfo file META-INF/cointainer.xml"""
"""Write the metainfo file META-INF/cointainer.xml."""
self.info('writing %s file...' % outname)
fn = path.join(outdir, outname)
try:
@ -248,7 +249,8 @@ class EpubBuilder(StandaloneHTMLBuilder):
def content_metadata(self, files, spine):
"""Create a dictionary with all metadata for the content.opf
file properly escaped"""
file properly escaped.
"""
metadata = {}
metadata['title'] = self.esc(self.config.epub_title)
metadata['author'] = self.esc(self.config.epub_author)
@ -263,9 +265,9 @@ class EpubBuilder(StandaloneHTMLBuilder):
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)."""
"""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
@ -317,7 +319,7 @@ class EpubBuilder(StandaloneHTMLBuilder):
f.close()
def new_navpoint(self, node, level, incr=True):
"""Create a new entry in the toc from the node at given level"""
"""Create a new entry in the toc from the node at given level."""
# XXX Modifies the node
if incr:
self.playorder += 1
@ -327,16 +329,19 @@ class EpubBuilder(StandaloneHTMLBuilder):
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"""
"""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
"""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."""
For nested nodes the parent node is reinserted in the subnav.
"""
navstack = []
navlist = []
level = 1
@ -372,7 +377,8 @@ class EpubBuilder(StandaloneHTMLBuilder):
def toc_metadata(self, level, navpoints):
"""Create a dictionary with all metadata for the toc.ncx
file properly escaped"""
file properly escaped.
"""
metadata = {}
metadata['uid'] = self.config.epub_uid
metadata['title'] = self.config.epub_title
@ -381,7 +387,7 @@ class EpubBuilder(StandaloneHTMLBuilder):
return metadata
def build_toc(self, outdir, outname):
"""Write the metainfo file toc.ncx"""
"""Write the metainfo file toc.ncx."""
self.info('writing %s file...' % outname)
navpoints = self.build_navpoints(self.refnodes)
@ -393,9 +399,11 @@ class EpubBuilder(StandaloneHTMLBuilder):
f.close()
def build_epub(self, outdir, outname):
"""Write the epub file
"""Write the epub file.
It is a zip file with the mimetype file stored uncompressed
as the first entry."""
as the first entry.
"""
self.info('writing %s file...' % outname)
projectfiles = ['META-INF/container.xml', 'content.opf', 'toc.ncx'] \
+ self.files
@ -406,4 +414,3 @@ class EpubBuilder(StandaloneHTMLBuilder):
for file in projectfiles:
epub.write(path.join(outdir, file), file, zipfile.ZIP_DEFLATED)
epub.close()

View File

@ -101,9 +101,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)
@ -279,8 +284,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):

View File

@ -97,6 +97,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),
@ -112,20 +127,6 @@ 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):

View File

@ -227,39 +227,23 @@ latex_documents = [
# -- 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.
# Bibliographic Dublin Core info.
#epub_title = ''
# The author of the text. The author is inserted into the
# bibliographic Dublin Core description of the content.opf file.
#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 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 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 = ''
# The scheme of the identifier. Typical schemes are ISBN or URL.
#epub_scheme = ''
# A unique identification for the text.
#epub_uid = ''