diff --git a/CHANGES b/CHANGES index e7de2402b..e6c73dc87 100644 --- a/CHANGES +++ b/CHANGES @@ -142,6 +142,7 @@ Features added * #3784: mathjax: Add :confval:`mathjax_options` to give options to script tag for mathjax * #4362: latex: Don't overwrite .tex file if document not changed +* #1431: latex: Add alphanumeric enumerated list support Bugs fixed ---------- diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index e4fdf3ca1..614feb219 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -71,6 +71,14 @@ HYPERLINK_SUPPORT_NODES = ( nodes.section, captioned_literal_block, ) +ENUMERATE_LIST_STYLE = defaultdict(lambda: r'\arabic', + { + 'arabic': r'\arabic', + 'loweralpha': r'\alph', + 'upperalpha': r'\Alph', + 'lowerroman': r'\roman', + 'upperroman': r'\Roman', + }) # type: Dict[unicode, unicode] DEFAULT_SETTINGS = { 'latex_engine': 'pdflatex', @@ -1478,6 +1486,15 @@ class LaTeXTranslator(nodes.NodeVisitor): def visit_enumerated_list(self, node): # type: (nodes.Node) -> None + def get_enumtype(node): + # type: (nodes.Node) -> unicode + enumtype = node.get('enumtype', 'arabic') + if 'alpha' in enumtype and 26 < node.get('start', 0) + len(node): + # fallback to arabic if alphabet counter overflows + enumtype = 'arabic' + + return enumtype + def get_nested_level(node): # type: (nodes.Node) -> int if node is None: @@ -1487,10 +1504,18 @@ class LaTeXTranslator(nodes.NodeVisitor): else: return get_nested_level(node.parent) + enum = "enum%s" % toRoman(get_nested_level(node)).lower() + enumnext = "enum%s" % toRoman(get_nested_level(node) + 1).lower() + style = ENUMERATE_LIST_STYLE.get(get_enumtype(node)) + self.body.append('\\begin{enumerate}\n') + self.body.append('\\def\\the%s{%s{%s}}\n' % (enum, style, enum)) + self.body.append('\\def\\label%s{%s\\the%s %s}\n' % + (enum, node['prefix'], enum, node['suffix'])) + self.body.append('\\makeatletter\\def\\p@%s{\\p@%s %s\\the%s %s}\\makeatother\n' % + (enumnext, enum, node['prefix'], enum, node['suffix'])) if 'start' in node: - enum_depth = "enum%s" % toRoman(get_nested_level(node)).lower() - self.body.append('\\setcounter{%s}{%d}\n' % (enum_depth, node['start'] - 1)) + self.body.append('\\setcounter{%s}{%d}\n' % (enum, node['start'] - 1)) if self.table: self.table.has_problematic = True diff --git a/tests/test_build_latex.py b/tests/test_build_latex.py index 56c1329f7..2573a74da 100644 --- a/tests/test_build_latex.py +++ b/tests/test_build_latex.py @@ -1229,11 +1229,26 @@ def test_latex_nested_enumerated_list(app, status, warning): app.builder.build_all() result = (app.outdir / 'test.tex').text(encoding='utf8') - assert r'\setcounter{enumi}{4}' in result - assert r'\setcounter{enumii}{3}' in result - assert r'\setcounter{enumiii}{9}' in result - assert r'\setcounter{enumiv}{23}' in result - assert r'\setcounter{enumii}{2}' in result + assert ('\\def\\theenumi{\\arabic{enumi}}\n' + '\\def\\labelenumi{\\theenumi .}\n' + '\\makeatletter\\def\\p@enumii{\\p@enumi \\theenumi .}\\makeatother\n' + '\\setcounter{enumi}{4}\n' in result) + assert ('\\def\\theenumii{\\alph{enumii}}\n' + '\\def\\labelenumii{\\theenumii .}\n' + '\\makeatletter\\def\\p@enumiii{\\p@enumii \\theenumii .}\\makeatother\n' + '\\setcounter{enumii}{3}\n' in result) + assert ('\\def\\theenumiii{\\arabic{enumiii}}\n' + '\\def\\labelenumiii{\\theenumiii )}\n' + '\\makeatletter\\def\\p@enumiv{\\p@enumiii \\theenumiii )}\\makeatother\n' + '\\setcounter{enumiii}{9}\n' in result) + assert ('\\def\\theenumiv{\\arabic{enumiv}}\n' + '\\def\\labelenumiv{(\\theenumiv )}\n' + '\\makeatletter\\def\\p@enumv{\\p@enumiv (\\theenumiv )}\\makeatother\n' + '\\setcounter{enumiv}{23}\n' in result) + assert ('\\def\\theenumii{\\roman{enumii}}\n' + '\\def\\labelenumii{\\theenumii .}\n' + '\\makeatletter\\def\\p@enumiii{\\p@enumii \\theenumii .}\\makeatother\n' + '\\setcounter{enumii}{2}\n' in result) @pytest.mark.sphinx('latex', testroot='footnotes')