summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGeorg Brandl <georg@python.org>2010-02-19 10:36:59 +0100
committerGeorg Brandl <georg@python.org>2010-02-19 10:36:59 +0100
commit2d386e35627a3c30c0133a78c079d416af8ac379 (patch)
tree84de1c593dba390696aeea5162621332ea65ea18
parentce67f3457c9bb608fc70eb09bb000190059a5105 (diff)
parent7b4b7b2a067a6258571e2e29ea423a85908fa759 (diff)
downloadsphinx-2d386e35627a3c30c0133a78c079d416af8ac379.tar.gz
merge with trunk
-rw-r--r--doc/builders.rst5
-rw-r--r--doc/config.rst6
-rw-r--r--sphinx/builders/epub.py73
-rw-r--r--sphinx/config.py1
-rw-r--r--sphinx/quickstart.py3
-rw-r--r--sphinx/setup_command.py56
-rw-r--r--sphinx/themes/epub/static/epub.css13
7 files changed, 126 insertions, 31 deletions
diff --git a/doc/builders.rst b/doc/builders.rst
index 4beb5bff..f609ab71 100644
--- a/doc/builders.rst
+++ b/doc/builders.rst
@@ -81,6 +81,11 @@ The builder's "name" must be given to the **-b** command-line option of
details about it. For definition of the epub format, have a look at
`<http://www.idpf.org/specs.htm>`_ or `<http://en.wikipedia.org/wiki/EPUB>`_.
+ Some ebook readers do not show the link targets of references. Therefore
+ this builder adds the targets after the link when necessary. The display
+ of the URLs can be customized by adding CSS rules for the class
+ ``link-target``.
+
Its name is ``epub``.
.. module:: sphinx.builders.latex
diff --git a/doc/config.rst b/doc/config.rst
index 01d73c97..f17071b8 100644
--- a/doc/config.rst
+++ b/doc/config.rst
@@ -709,6 +709,12 @@ the `Dublin Core metadata <http://dublincore.org/>`_.
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 ``[]``.
+.. confval:: epub_tocdepth
+
+ The depth of the table of contents in the file :file:`toc.ncx`. It should
+ be an integer greater than zero. The default value is 3. Note: A deeply
+ nested table of contents may be difficult to navigate.
+
.. _latex-options:
diff --git a/sphinx/builders/epub.py b/sphinx/builders/epub.py
index 6694c8af..9767391e 100644
--- a/sphinx/builders/epub.py
+++ b/sphinx/builders/epub.py
@@ -16,6 +16,7 @@ from os import path
import zipfile
from docutils import nodes
+from docutils.transforms import Transform
from sphinx.builders.html import StandaloneHTMLBuilder
from sphinx.util.osutil import EEXIST
@@ -23,6 +24,9 @@ from sphinx.util.osutil import EEXIST
# (Fragment) templates from which the metainfo files content.opf, toc.ncx,
# mimetype, and META-INF/container.xml are created.
+# This template section also defines strings that are embedded in the html
+# output but that may be customized by (re-)setting module attributes,
+# e.g. from conf.py.
_mimetype_template = 'application/epub+zip' # no EOL!
@@ -99,6 +103,10 @@ _spine_template = u'''\
_toctree_template = u'toctree-l%d'
+_link_target_template = u' [%(uri)s]'
+
+_css_link_target_class = u'link-target'
+
_media_types = {
'.html': 'application/xhtml+xml',
'.css': 'text/css',
@@ -112,6 +120,30 @@ _media_types = {
}
+# The transform to show link targets
+
+class VisibleLinksTransform(Transform):
+ """
+ Add the link target of referances to the text, unless it is already
+ present in the description.
+ """
+
+ # This transform must run after the references transforms
+ default_priority = 680
+
+ def apply(self):
+ for ref in self.document.traverse(nodes.reference):
+ uri = ref.get('refuri', '')
+ if ( uri.startswith('http:') or uri.startswith('https:') or \
+ uri.startswith('ftp:') ) and uri not in ref.astext():
+ uri = _link_target_template % {'uri': uri}
+ if uri:
+ idx = ref.parent.index(ref) + 1
+ link = nodes.inline(uri, uri)
+ link['classes'].append(_css_link_target_class)
+ ref.parent.insert(idx, link)
+
+
# The epub publisher
class EpubBuilder(StandaloneHTMLBuilder):
@@ -138,6 +170,7 @@ class EpubBuilder(StandaloneHTMLBuilder):
# the output files for epub must be .html only
self.out_suffix = '.html'
self.playorder = 0
+ self.app.add_transform(VisibleLinksTransform)
def get_theme_config(self):
return self.config.epub_theme, {}
@@ -145,7 +178,7 @@ class EpubBuilder(StandaloneHTMLBuilder):
# generic support functions
def make_id(self, name):
"""Replace all characters not allowed for (X)HTML ids."""
- return name.replace('/', '_')
+ return name.replace('/', '_').replace(' ', '')
def esc(self, name):
"""Replace all characters not allowed in text an attribute values."""
@@ -157,34 +190,20 @@ class EpubBuilder(StandaloneHTMLBuilder):
name = name.replace('\'', '&apos;')
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?
+ # toctree-l[1-8] 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)?
+ for l in range(8, 0, -1): # or range(1, 8)?
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, [])))
+ 'text': self.esc(doctree.astext())
})
else:
for elem in doctree.children:
@@ -195,16 +214,14 @@ class EpubBuilder(StandaloneHTMLBuilder):
"""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)
+ doctree = self.env.get_and_resolve_doctree(self.config.master_doc,
+ self, prune_toctrees=False)
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], []
- ))),
+ 'text': self.esc(self.env.titles[self.config.master_doc].astext())
})
- # XXX: is reversed ok?
for file, text in reversed(self.config.epub_pre_files):
self.refnodes.insert(0, {
'level': 1,
@@ -290,11 +307,10 @@ class EpubBuilder(StandaloneHTMLBuilder):
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)
+ self.warn('unknown mimetype for %s, ignoring' % filename)
continue
projectfiles.append(_file_template % {
'href': self.esc(filename),
@@ -338,7 +354,7 @@ class EpubBuilder(StandaloneHTMLBuilder):
"""Insert nested navpoints for given node.
The node and subnav are already rendered to text.
"""
- nlist = node.split('\n')
+ nlist = node.rsplit('\n', 1)
nlist.insert(-1, subnav)
return '\n'.join(nlist)
@@ -356,8 +372,10 @@ class EpubBuilder(StandaloneHTMLBuilder):
file = node['refuri'].split('#')[0]
if file in self.ignored_files:
continue
+ if node['level'] > self.config.epub_tocdepth:
+ continue
if node['level'] == level:
- navlist.append(self.new_navpoint(node,level))
+ navlist.append(self.new_navpoint(node, level))
elif node['level'] == level + 1:
navstack.append(navlist)
navlist = []
@@ -398,6 +416,7 @@ class EpubBuilder(StandaloneHTMLBuilder):
navpoints = self.build_navpoints(self.refnodes)
level = max(item['level'] for item in self.refnodes)
+ level = min(level, self.config.epub_tocdepth)
f = codecs.open(path.join(outdir, outname), 'w', 'utf-8')
try:
f.write(_toc_template % self.toc_metadata(level, navpoints))
diff --git a/sphinx/config.py b/sphinx/config.py
index fca33136..f6f506bc 100644
--- a/sphinx/config.py
+++ b/sphinx/config.py
@@ -118,6 +118,7 @@ class Config(object):
epub_pre_files = ([], 'env'),
epub_post_files = ([], 'env'),
epub_exclude_files = ([], 'env'),
+ epub_tocdepth = (3, 'env'),
# LaTeX options
latex_documents = ([], None),
diff --git a/sphinx/quickstart.py b/sphinx/quickstart.py
index f3d26aa4..73fb968d 100644
--- a/sphinx/quickstart.py
+++ b/sphinx/quickstart.py
@@ -258,6 +258,9 @@ latex_documents = [
# A list of files that should not be packed into the epub file.
#epub_exclude_files = []
+
+# The depth of the table of contents in toc.ncx.
+#epub_tocdepth = 3
'''
INTERSPHINX_CONFIG = '''
diff --git a/sphinx/setup_command.py b/sphinx/setup_command.py
index d569b031..d4f87348 100644
--- a/sphinx/setup_command.py
+++ b/sphinx/setup_command.py
@@ -22,7 +22,41 @@ from sphinx.util.console import darkred, nocolor, color_terminal
class BuildDoc(Command):
- """Distutils command to build Sphinx documentation."""
+ """Distutils command to build Sphinx documentation.
+
+ The Sphinx build can then be triggered from distutils, and some Sphinx
+ options can be set in ``setup.py`` or ``setup.cfg`` instead of Sphinx own
+ configuration file.
+
+ For instance, from `setup.py`::
+
+ # this is only necessary when not using setuptools/distribute
+ from sphinx.setup_command import BuildDoc
+ cmdclass = {'build_sphinx': BuildDoc}
+
+ name = 'My project'
+ version = 1.2
+ release = 1.2.0
+ setup(
+ name=name,
+ author='Bernard Montgomery',
+ version=release,
+ cmdclass={'build_sphinx': BuildDoc},
+ # these are optional and override conf.py settings
+ command_options={
+ 'build_sphinx': {
+ 'project': ('setup.py', name),
+ 'version': ('setup.py', version),
+ 'release': ('setup.py', release)}},
+ )
+
+ Or add this section in ``setup.cfg``::
+
+ [build_sphinx]
+ project = 'My project'
+ version = 1.2
+ release = 1.2.0
+ """
description = 'Build Sphinx documentation'
user_options = [
@@ -31,15 +65,22 @@ class BuildDoc(Command):
('source-dir=', 's', 'Source directory'),
('build-dir=', None, 'Build directory'),
('builder=', 'b', 'The builder to use. Defaults to "html"'),
- ]
+ ('project=', None, 'The documented project\'s name'),
+ ('version=', None, 'The short X.Y version'),
+ ('release=', None, 'The full version, including alpha/beta/rc tags'),
+ ('today=', None, 'How to format the current date, used as the '
+ 'replacement for |today|'),
+ ]
boolean_options = ['fresh-env', 'all-files']
def initialize_options(self):
self.fresh_env = self.all_files = False
self.source_dir = self.build_dir = None
- self.conf_file_name = 'conf.py'
self.builder = 'html'
+ self.version = ''
+ self.release = ''
+ self.today = ''
def _guess_source_dir(self):
for guess in ('doc', 'docs'):
@@ -74,9 +115,16 @@ class BuildDoc(Command):
status_stream = StringIO()
else:
status_stream = sys.stdout
+ confoverrides = {}
+ if self.version:
+ confoverrides['version'] = self.version
+ if self.release:
+ confoverrides['release'] = self.release
+ if self.today:
+ confoverrides['today'] = self.today
app = Sphinx(self.source_dir, self.source_dir,
self.builder_target_dir, self.doctree_dir,
- self.builder, {}, status_stream,
+ self.builder, confoverrides, status_stream,
freshenv=self.fresh_env)
try:
diff --git a/sphinx/themes/epub/static/epub.css b/sphinx/themes/epub/static/epub.css
index f941b79a..c6320c8c 100644
--- a/sphinx/themes/epub/static/epub.css
+++ b/sphinx/themes/epub/static/epub.css
@@ -420,6 +420,19 @@ div.footer a {
text-decoration: underline;
}
+/* -- link-target ----------------------------------------------------------- */
+
+.link-target {
+ font-size: 80%;
+}
+
+table .link-target {
+ /* Do not show links in tables, there is not enough space */
+ display: none;
+}
+
+/* -- font-face ------------------------------------------------------------- */
+
@font-face {
font-family: "LiberationNarrow";
font-style: normal;