diff --git a/sphinx/builders/epub3.py b/sphinx/builders/epub3.py index e1d7bc9a23..4cea39956d 100644 --- a/sphinx/builders/epub3.py +++ b/sphinx/builders/epub3.py @@ -10,14 +10,15 @@ :license: BSD, see LICENSE for details. """ -import codecs from os import path from datetime import datetime +from collections import namedtuple from sphinx import package_dir from sphinx.config import string_classes from sphinx.builders.epub import EpubBuilder from sphinx.util import logging +from sphinx.util.fileutil import copy_asset_file if False: # For type annotation @@ -28,38 +29,7 @@ if False: logger = logging.getLogger(__name__) -# (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. - -NAVIGATION_DOC_TEMPLATE = u'''\ - - - - - %(toc_locale)s - - - - - -''' - -NAVLIST_TEMPLATE = u'''%(indent)s
  • %(text)s
  • ''' -NAVLIST_TEMPLATE_HAS_CHILD = u'''%(indent)s
  • %(text)s''' -NAVLIST_TEMPLATE_BEGIN_BLOCK = u'''%(indent)s
      ''' -NAVLIST_TEMPLATE_END_BLOCK = u'''%(indent)s
    -%(indent)s
  • ''' -NAVLIST_INDENT = ' ' - +NavPoint = namedtuple('NavPoint', ['text', 'refuri', 'children']) DOCTYPE = u'''''' @@ -77,13 +47,6 @@ class Epub3Builder(EpubBuilder): name = 'epub' template_dir = path.join(package_dir, 'templates', 'epub3') - - navigation_doc_template = NAVIGATION_DOC_TEMPLATE - navlist_template = NAVLIST_TEMPLATE - navlist_template_has_child = NAVLIST_TEMPLATE_HAS_CHILD - navlist_template_begin_block = NAVLIST_TEMPLATE_BEGIN_BLOCK - navlist_template_end_block = NAVLIST_TEMPLATE_END_BLOCK - navlist_indent = NAVLIST_INDENT doctype = DOCTYPE # Finish by building the epub file @@ -145,28 +108,8 @@ class Epub3Builder(EpubBuilder): super(Epub3Builder, self).prepare_writing(docnames) self.globalcontext['theme_writing_mode'] = self._css_writing_mode() - def new_navlist(self, node, level, has_child): - # type: (nodes.Node, int, bool) -> unicode - """Create a new entry in the toc from the node at given level.""" - # XXX Modifies the node - node['indent'] = self.navlist_indent * level - if has_child: - return self.navlist_template_has_child % node - else: - return self.navlist_template % node - - def begin_navlist_block(self, level): - # type: (int) -> unicode - return self.navlist_template_begin_block % { - "indent": self.navlist_indent * level - } - - def end_navlist_block(self, level): - # type: (int) -> unicode - return self.navlist_template_end_block % {"indent": self.navlist_indent * level} - def build_navlist(self, navnodes): - # type: (List[nodes.Node]) -> unicode + # type: (List[nodes.Node]) -> List[NavPoint] """Create the toc navigation structure. This method is almost same as build_navpoints method in epub.py. @@ -176,9 +119,9 @@ class Epub3Builder(EpubBuilder): The difference from build_navpoints method is templates which are used when generating navigation documents. """ - navlist = [] - level = 1 - usenodes = [] + navstack = [] # type: List[NavPoint] + navstack.append(NavPoint('', '', [])) + level = 0 for node in navnodes: if not node['text']: continue @@ -187,31 +130,33 @@ class Epub3Builder(EpubBuilder): continue if node['level'] > self.config.epub_tocdepth: continue - usenodes.append(node) - for i, node in enumerate(usenodes): - curlevel = node['level'] - if curlevel == level + 1: - navlist.append(self.begin_navlist_block(level)) - while curlevel < level: - level -= 1 - navlist.append(self.end_navlist_block(level)) - level = curlevel - if i != len(usenodes) - 1 and usenodes[i + 1]['level'] > level: - has_child = True + + navpoint = NavPoint(node['text'], node['refuri'], []) + if node['level'] == level: + navstack.pop() + navstack[-1].children.append(navpoint) + navstack.append(navpoint) + elif node['level'] == level + 1: + level += 1 + navstack[-1].children.append(navpoint) + navstack.append(navpoint) + elif node['level'] < level: + while node['level'] < len(navstack): + navstack.pop() + level = node['level'] + navstack[-1].children.append(navpoint) + navstack.append(navpoint) else: - has_child = False - navlist.append(self.new_navlist(node, level, has_child)) - while level != 1: - level -= 1 - navlist.append(self.end_navlist_block(level)) - return '\n'.join(navlist) + raise + + return navstack[0].children def navigation_doc_metadata(self, navlist): - # type: (unicode) -> Dict + # type: (List[NavPoint]) -> Dict """Create a dictionary with all metadata for the nav.xhtml file properly escaped. """ - metadata = {} + metadata = {} # type: Dict metadata['lang'] = self.esc(self.config.epub_language) metadata['toc_locale'] = self.esc(self.guide_titles['toc']) metadata['navlist'] = navlist @@ -232,9 +177,9 @@ class Epub3Builder(EpubBuilder): # 'includehidden' refnodes = self.refnodes navlist = self.build_navlist(refnodes) - with codecs.open(path.join(outdir, outname), 'w', 'utf-8') as f: # type: ignore - f.write(self.navigation_doc_template % # type: ignore - self.navigation_doc_metadata(navlist)) + copy_asset_file(path.join(self.template_dir, 'nav.xhtml_t'), + path.join(outdir, outname), + self.navigation_doc_metadata(navlist)) # Add nav.xhtml to epub file if outname not in self.files: diff --git a/sphinx/templates/epub3/nav.xhtml_t b/sphinx/templates/epub3/nav.xhtml_t new file mode 100644 index 0000000000..2a32c20391 --- /dev/null +++ b/sphinx/templates/epub3/nav.xhtml_t @@ -0,0 +1,26 @@ +{%- macro toctree(navlist) -%} +
      +{%- for nav in navlist %} +
    1. + {{ nav.text }} + {%- if nav.children %} +{{ toctree(nav.children)|indent(4, true) }} + {%- endif %} +
    2. +{%- endfor %} +
    +{%- endmacro -%} + + + + + {{ toc_locale }} + + + + +