summaryrefslogtreecommitdiff
path: root/sphinx/writers/latex.py
diff options
context:
space:
mode:
Diffstat (limited to 'sphinx/writers/latex.py')
-rw-r--r--sphinx/writers/latex.py1121
1 files changed, 550 insertions, 571 deletions
diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py
index 3ada12c4d..ec5fcde09 100644
--- a/sphinx/writers/latex.py
+++ b/sphinx/writers/latex.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
"""
sphinx.writers.latex
~~~~~~~~~~~~~~~~~~~~
@@ -17,18 +16,20 @@ import sys
import warnings
from collections import defaultdict
from os import path
+from typing import Iterable, cast
from docutils import nodes, writers
-from docutils.writers.latex2e import Babel
-from six import itervalues, text_type
from sphinx import addnodes
from sphinx import highlighting
-from sphinx.deprecation import RemovedInSphinx30Warning
+from sphinx.deprecation import (
+ RemovedInSphinx30Warning, RemovedInSphinx40Warning, deprecated_alias
+)
+from sphinx.domains.std import StandardDomain
from sphinx.errors import SphinxError
from sphinx.locale import admonitionlabels, _, __
from sphinx.util import split_into, logging
-from sphinx.util.i18n import format_date
+from sphinx.util.docutils import SphinxTranslator
from sphinx.util.nodes import clean_astext
from sphinx.util.template import LaTeXRenderer
from sphinx.util.texescape import tex_escape_map, tex_replace_map
@@ -37,12 +38,16 @@ try:
from docutils.utils.roman import toRoman
except ImportError:
# In Debain/Ubuntu, roman package is provided as roman, not as docutils.utils.roman
- from roman import toRoman
+ from roman import toRoman # type: ignore
if False:
# For type annotation
from typing import Any, Callable, Dict, Iterator, List, Pattern, Tuple, Set, Union # NOQA
- from sphinx.builder import Builder # NOQA
+ from sphinx.builders.latex import LaTeXBuilder # NOQA
+ from sphinx.builders.latex.nodes import ( # NOQA
+ captioned_literal_block, footnotemark, footnotetext, math_reference, thebibliography
+ )
+ from sphinx.domains import IndexEntry # NOQA
logger = logging.getLogger(__name__)
@@ -63,7 +68,55 @@ ENUMERATE_LIST_STYLE = defaultdict(lambda: r'\arabic',
'upperalpha': r'\Alph',
'lowerroman': r'\roman',
'upperroman': r'\Roman',
- }) # type: Dict[unicode, unicode]
+ })
+PDFLATEX_DEFAULT_FONTPKG = r'''
+\usepackage{times}
+\expandafter\ifx\csname T@LGR\endcsname\relax
+\else
+% LGR was declared as font encoding
+ \substitutefont{LGR}{\rmdefault}{cmr}
+ \substitutefont{LGR}{\sfdefault}{cmss}
+ \substitutefont{LGR}{\ttdefault}{cmtt}
+\fi
+\expandafter\ifx\csname T@X2\endcsname\relax
+ \expandafter\ifx\csname T@T2A\endcsname\relax
+ \else
+ % T2A was declared as font encoding
+ \substitutefont{T2A}{\rmdefault}{cmr}
+ \substitutefont{T2A}{\sfdefault}{cmss}
+ \substitutefont{T2A}{\ttdefault}{cmtt}
+ \fi
+\else
+% X2 was declared as font encoding
+ \substitutefont{X2}{\rmdefault}{cmr}
+ \substitutefont{X2}{\sfdefault}{cmss}
+ \substitutefont{X2}{\ttdefault}{cmtt}
+\fi
+'''
+XELATEX_DEFAULT_FONTPKG = r'''
+\setmainfont{FreeSerif}[
+ Extension = .otf,
+ UprightFont = *,
+ ItalicFont = *Italic,
+ BoldFont = *Bold,
+ BoldItalicFont = *BoldItalic
+]
+\setsansfont{FreeSans}[
+ Extension = .otf,
+ UprightFont = *,
+ ItalicFont = *Oblique,
+ BoldFont = *Bold,
+ BoldItalicFont = *BoldOblique,
+]
+\setmonofont{FreeMono}[
+ Extension = .otf,
+ UprightFont = *,
+ ItalicFont = *Oblique,
+ BoldFont = *Bold,
+ BoldItalicFont = *BoldOblique,
+]
+'''
+LUALATEX_DEFAULT_FONTPKG = XELATEX_DEFAULT_FONTPKG
DEFAULT_SETTINGS = {
'latex_engine': 'pdflatex',
@@ -86,7 +139,10 @@ DEFAULT_SETTINGS = {
'multilingual': '',
'babel': '\\usepackage{babel}',
'polyglossia': '',
- 'fontpkg': '\\usepackage{times}',
+ 'fontpkg': PDFLATEX_DEFAULT_FONTPKG,
+ 'substitutefont': '',
+ 'textcyrillic': '',
+ 'textgreek': '\\usepackage{textalpha}',
'fncychap': '\\usepackage[Bjarne]{fncychap}',
'hyperref': ('% Include hyperref last.\n'
'\\usepackage{hyperref}\n'
@@ -94,15 +150,11 @@ DEFAULT_SETTINGS = {
'\\usepackage{hypcap}% it must be loaded after hyperref.\n'
'% Set up styles of URL: it should be placed after hyperref.\n'
'\\urlstyle{same}'),
- 'usepackages': '',
- 'numfig_format': '',
'contentsname': '',
'preamble': '',
'title': '',
- 'date': '',
'release': '',
'author': '',
- 'logo': '\\vbox{}',
'releasename': '',
'makeindex': '\\makeindex',
'shorthandoff': '',
@@ -114,30 +166,24 @@ DEFAULT_SETTINGS = {
'figure_align': 'htbp',
'tocdepth': '',
'secnumdepth': '',
- 'pageautorefname': '',
- 'translatablestrings': '',
-} # type: Dict[unicode, unicode]
+} # type: Dict[str, Any]
ADDITIONAL_SETTINGS = {
'pdflatex': {
'inputenc': '\\usepackage[utf8]{inputenc}',
'utf8extra': ('\\ifdefined\\DeclareUnicodeCharacter\n'
'% support both utf8 and utf8x syntaxes\n'
- '\\edef\\sphinxdqmaybe{'
- '\\ifdefined\\DeclareUnicodeCharacterAsOptional'
- '\\string"\\fi}\n'
- ' \\DeclareUnicodeCharacter{\\sphinxdqmaybe00A0}'
- '{\\nobreakspace}\n'
- ' \\DeclareUnicodeCharacter{\\sphinxdqmaybe2500}'
- '{\\sphinxunichar{2500}}\n'
- ' \\DeclareUnicodeCharacter{\\sphinxdqmaybe2502}'
- '{\\sphinxunichar{2502}}\n'
- ' \\DeclareUnicodeCharacter{\\sphinxdqmaybe2514}'
- '{\\sphinxunichar{2514}}\n'
- ' \\DeclareUnicodeCharacter{\\sphinxdqmaybe251C}'
- '{\\sphinxunichar{251C}}\n'
- ' \\DeclareUnicodeCharacter{\\sphinxdqmaybe2572}'
- '{\\textbackslash}\n'
+ ' \\ifdefined\\DeclareUnicodeCharacterAsOptional\n'
+ ' \\def\\sphinxDUC#1{\\DeclareUnicodeCharacter{"#1}}\n'
+ ' \\else\n'
+ ' \\let\\sphinxDUC\\DeclareUnicodeCharacter\n'
+ ' \\fi\n'
+ ' \\sphinxDUC{00A0}{\\nobreakspace}\n'
+ ' \\sphinxDUC{2500}{\\sphinxunichar{2500}}\n'
+ ' \\sphinxDUC{2502}{\\sphinxunichar{2502}}\n'
+ ' \\sphinxDUC{2514}{\\sphinxunichar{2514}}\n'
+ ' \\sphinxDUC{251C}{\\sphinxunichar{251C}}\n'
+ ' \\sphinxDUC{2572}{\\textbackslash}\n'
'\\fi'),
},
'xelatex': {
@@ -145,29 +191,32 @@ ADDITIONAL_SETTINGS = {
'polyglossia': '\\usepackage{polyglossia}',
'babel': '',
'fontenc': '\\usepackage{fontspec}',
- 'fontpkg': '',
+ 'fontpkg': XELATEX_DEFAULT_FONTPKG,
+ 'textgreek': '',
'utf8extra': ('\\catcode`^^^^00a0\\active\\protected\\def^^^^00a0'
'{\\leavevmode\\nobreak\\ }'),
- 'fvset': '\\fvset{fontsize=auto}',
},
'lualatex': {
'latex_engine': 'lualatex',
'polyglossia': '\\usepackage{polyglossia}',
'babel': '',
- 'fontenc': '\\usepackage{fontspec}',
- 'fontpkg': '',
+ 'fontenc': ('\\usepackage{fontspec}\n'
+ '\\defaultfontfeatures[\\rmfamily,\\sffamily]{}'),
+ 'fontpkg': LUALATEX_DEFAULT_FONTPKG,
+ 'textgreek': '',
'utf8extra': ('\\catcode`^^^^00a0\\active\\protected\\def^^^^00a0'
'{\\leavevmode\\nobreak\\ }'),
- 'fvset': '\\fvset{fontsize=auto}',
},
'platex': {
'latex_engine': 'platex',
'babel': '',
'classoptions': ',dvipdfmx',
+ 'fontpkg': '\\usepackage{times}',
+ 'textgreek': '',
'fncychap': '',
'geometry': '\\usepackage[dvipdfm]{geometry}',
},
-} # type: Dict[unicode, Dict[unicode, unicode]]
+} # type: Dict[str, Dict[str, Any]]
EXTRA_RE = re.compile(r'^(.*\S)\s+\(([^()]*)\)\s*$')
@@ -194,86 +243,34 @@ class LaTeXWriter(writers.Writer):
output = None
def __init__(self, builder):
- # type: (Builder) -> None
- writers.Writer.__init__(self)
+ # type: (LaTeXBuilder) -> None
+ super().__init__()
self.builder = builder
def translate(self):
# type: () -> None
visitor = self.builder.create_translator(self.document, self.builder)
self.document.walkabout(visitor)
- self.output = visitor.astext()
+ self.output = cast(LaTeXTranslator, visitor).astext()
# Helper classes
-class ExtBabel(Babel):
- cyrillic_languages = ('bulgarian', 'kazakh', 'mongolian', 'russian', 'ukrainian')
-
- def __init__(self, language_code, use_polyglossia=False):
- # type: (unicode, bool) -> None
- self.language_code = language_code
- self.use_polyglossia = use_polyglossia
- self.supported = True
- super(ExtBabel, self).__init__(language_code or '')
-
- def get_shorthandoff(self):
- # type: () -> unicode
- warnings.warn('ExtBabel.get_shorthandoff() is deprecated.',
- RemovedInSphinx30Warning, stacklevel=2)
- return SHORTHANDOFF
-
- def uses_cyrillic(self):
- # type: () -> bool
- return self.language in self.cyrillic_languages
-
- def is_supported_language(self):
- # type: () -> bool
- return self.supported
-
- def language_name(self, language_code):
- # type: (unicode) -> unicode
- language = super(ExtBabel, self).language_name(language_code)
- if language == 'ngerman' and self.use_polyglossia:
- # polyglossia calls new orthography (Neue Rechtschreibung) as
- # german (with new spelling option).
- return 'german'
- elif not language:
- self.supported = False
- return 'english' # fallback to english
- else:
- return language
-
- def get_mainlanguage_options(self):
- # type: () -> unicode
- """Return options for polyglossia's ``\\setmainlanguage``."""
- if self.use_polyglossia is False:
- return None
- elif self.language == 'german':
- language = super(ExtBabel, self).language_name(self.language_code)
- if language == 'ngerman':
- return 'spelling=new'
- else:
- return 'spelling=old'
- else:
- return None
-
-
-class Table(object):
+class Table:
"""A table data"""
def __init__(self, node):
- # type: (nodes.table) -> None
- self.header = [] # type: List[unicode]
- self.body = [] # type: List[unicode]
+ # type: (nodes.Element) -> None
+ self.header = [] # type: List[str]
+ self.body = [] # type: List[str]
self.align = node.get('align')
self.colcount = 0
- self.colspec = None # type: unicode
+ self.colspec = None # type: str
self.colwidths = [] # type: List[int]
self.has_problematic = False
self.has_oldproblematic = False
self.has_verbatim = False
- self.caption = None # type: List[unicode]
+ self.caption = None # type: List[str]
self.stubs = [] # type: List[int]
# current position
@@ -281,7 +278,7 @@ class Table(object):
self.row = 0
# for internal use
- self.classes = node.get('classes', []) # type: List[unicode]
+ self.classes = node.get('classes', []) # type: List[str]
self.cells = defaultdict(int) # type: Dict[Tuple[int, int], int]
# it maps table location to cell_id
# (cell = rectangular area)
@@ -289,14 +286,14 @@ class Table(object):
@property
def caption_footnotetexts(self):
- # type: () -> List[unicode]
+ # type: () -> List[str]
warnings.warn('table.caption_footnotetexts is deprecated.',
RemovedInSphinx30Warning, stacklevel=2)
return []
@property
def header_footnotetexts(self):
- # type: () -> List[unicode]
+ # type: () -> List[str]
warnings.warn('table.header_footnotetexts is deprecated.',
RemovedInSphinx30Warning, stacklevel=2)
return []
@@ -307,7 +304,7 @@ class Table(object):
return self.row > 30 or 'longtable' in self.classes
def get_table_type(self):
- # type: () -> unicode
+ # type: () -> str
"""Returns the LaTeX environment name for the table.
The class currently supports:
@@ -328,7 +325,7 @@ class Table(object):
return 'tabulary'
def get_colspec(self):
- # type: () -> unicode
+ # type: () -> str
"""Returns a column spec of table.
This is what LaTeX calls the 'preamble argument' of the used table environment.
@@ -380,7 +377,7 @@ class Table(object):
return None
-class TableCell(object):
+class TableCell:
"""A cell data of tables."""
def __init__(self, table, row, col):
@@ -419,13 +416,13 @@ class TableCell(object):
def escape_abbr(text):
- # type: (unicode) -> unicode
+ # type: (str) -> str
"""Adjust spacing after abbreviations."""
return re.sub(r'\.(?=\s|$)', r'.\@', text)
def rstdim_to_latexdim(width_str, scale = 100):
- # type: (unicode, int) -> unicode
+ # type: (str, int) -> str
"""Convert `width_str` with rst length to LaTeX length."""
match = re.match(r'^(\d*\.?\d*)\s*(\S*)$', width_str)
if not match:
@@ -453,7 +450,8 @@ def rstdim_to_latexdim(width_str, scale = 100):
return res
-class LaTeXTranslator(nodes.NodeVisitor):
+class LaTeXTranslator(SphinxTranslator):
+ builder = None # type: LaTeXBuilder
secnumdepth = 2 # legacy sphinxhowto.cls uses this, whereas article.cls
# default is originally 3. For book/report, 2 is already LaTeX default.
@@ -463,10 +461,9 @@ class LaTeXTranslator(nodes.NodeVisitor):
docclasses = ('howto', 'manual')
def __init__(self, document, builder):
- # type: (nodes.Node, Builder) -> None
- nodes.NodeVisitor.__init__(self, document)
- self.builder = builder
- self.body = [] # type: List[unicode]
+ # type: (nodes.document, LaTeXBuilder) -> None
+ super().__init__(document, builder)
+ self.body = [] # type: List[str]
# flags
self.in_title = 0
@@ -485,65 +482,36 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.first_param = 0
# sort out some elements
- self.elements = DEFAULT_SETTINGS.copy()
- self.elements.update(ADDITIONAL_SETTINGS.get(builder.config.latex_engine, {}))
- # for xelatex+French, don't use polyglossia
- if self.elements['latex_engine'] == 'xelatex':
- if builder.config.language:
- if builder.config.language[:2] == 'fr':
- self.elements.update({
- 'polyglossia': '',
- 'babel': '\\usepackage{babel}',
- })
- # allow the user to override them all
- self.elements.update(builder.config.latex_elements)
+ self.elements = self.builder.context.copy()
# but some have other interface in config file
- self.elements.update({
- 'wrapperclass': self.format_docclass(document.settings.docclass),
- # if empty, the title is set to the first section title
- 'title': document.settings.title, # treat as a raw LaTeX code
- 'release': self.encode(builder.config.release),
- 'author': document.settings.author, # treat as a raw LaTeX code
- 'indexname': _('Index'),
- 'use_xindy': builder.config.latex_use_xindy,
- })
- if not self.elements['releasename'] and self.elements['release']:
- self.elements.update({
- 'releasename': _('Release'),
- })
+ self.elements['wrapperclass'] = self.format_docclass(self.settings.docclass)
# we assume LaTeX class provides \chapter command except in case
# of non-Japanese 'howto' case
self.sectionnames = LATEXSECTIONNAMES[:]
- if document.settings.docclass == 'howto':
- docclass = builder.config.latex_docclass.get('howto', 'article')
+ if self.settings.docclass == 'howto':
+ docclass = self.config.latex_docclass.get('howto', 'article')
if docclass[0] == 'j': # Japanese class...
pass
else:
self.sectionnames.remove('chapter')
else:
- docclass = builder.config.latex_docclass.get('manual', 'report')
+ docclass = self.config.latex_docclass.get('manual', 'report')
self.elements['docclass'] = docclass
# determine top section level
self.top_sectionlevel = 1
- if builder.config.latex_toplevel_sectioning:
+ if self.config.latex_toplevel_sectioning:
try:
self.top_sectionlevel = \
- self.sectionnames.index(builder.config.latex_toplevel_sectioning)
+ self.sectionnames.index(self.config.latex_toplevel_sectioning)
except ValueError:
logger.warning(__('unknown %r toplevel_sectioning for class %r') %
- (builder.config.latex_toplevel_sectioning, docclass))
-
- if builder.config.today:
- self.elements['date'] = builder.config.today
- else:
- self.elements['date'] = format_date(builder.config.today_fmt or _('%b %d, %Y'),
- language=builder.config.language)
+ (self.config.latex_toplevel_sectioning, docclass))
- if builder.config.numfig:
- self.numfig_secnum_depth = builder.config.numfig_secnum_depth
+ if self.config.numfig:
+ self.numfig_secnum_depth = self.config.numfig_secnum_depth
if self.numfig_secnum_depth > 0: # default is 1
# numfig_secnum_depth as passed to sphinx.sty indices same names as in
# LATEXSECTIONNAMES but with -1 for part, 0 for chapter, 1 for section...
@@ -561,44 +529,51 @@ class LaTeXTranslator(nodes.NodeVisitor):
else:
self.elements['sphinxpkgoptions'] += ',nonumfigreset'
try:
- if builder.config.math_numfig:
+ if self.config.math_numfig:
self.elements['sphinxpkgoptions'] += ',mathnumfig'
except AttributeError:
pass
- if builder.config.latex_logo:
- # no need for \\noindent here, used in flushright
- self.elements['logo'] = '\\sphinxincludegraphics{%s}\\par' % \
- path.basename(builder.config.latex_logo)
-
- if (builder.config.language and builder.config.language != 'ja' and
- 'fncychap' not in builder.config.latex_elements):
- # use Sonny style if any language specified
+ if (self.config.language not in {None, 'en', 'ja'} and
+ 'fncychap' not in self.config.latex_elements):
+ # use Sonny style if any language specified (except English)
self.elements['fncychap'] = ('\\usepackage[Sonny]{fncychap}\n'
'\\ChNameVar{\\Large\\normalfont'
'\\sffamily}\n\\ChTitleVar{\\Large'
'\\normalfont\\sffamily}')
- self.babel = ExtBabel(builder.config.language,
- not self.elements['babel'])
- if builder.config.language and not self.babel.is_supported_language():
+ self.babel = self.builder.babel
+ if self.config.language and not self.babel.is_supported_language():
# emit warning if specified language is invalid
# (only emitting, nothing changed to processing)
logger.warning(__('no Babel option known for language %r'),
- builder.config.language)
+ self.config.language)
# set up multilingual module...
+ if self.elements['latex_engine'] == 'pdflatex':
+ if not self.babel.uses_cyrillic():
+ if 'X2' in self.elements['fontenc']:
+ self.elements['substitutefont'] = '\\usepackage{substitutefont}'
+ self.elements['textcyrillic'] = ('\\usepackage[Xtwo]'
+ '{sphinxcyrillic}')
+ elif 'T2A' in self.elements['fontenc']:
+ self.elements['substitutefont'] = '\\usepackage{substitutefont}'
+ self.elements['textcyrillic'] = ('\\usepackage[TtwoA]'
+ '{sphinxcyrillic}')
+ if 'LGR' in self.elements['fontenc']:
+ self.elements['substitutefont'] = '\\usepackage{substitutefont}'
+ else:
+ self.elements['textgreek'] = ''
# 'babel' key is public and user setting must be obeyed
if self.elements['babel']:
self.elements['classoptions'] += ',' + self.babel.get_language()
# this branch is not taken for xelatex/lualatex if default settings
self.elements['multilingual'] = self.elements['babel']
- if builder.config.language:
+ if self.config.language:
self.elements['shorthandoff'] = SHORTHANDOFF
# Times fonts don't work with Cyrillic languages
- if self.babel.uses_cyrillic() \
- and 'fontpkg' not in builder.config.latex_elements:
+ if self.babel.uses_cyrillic() and 'fontpkg' not in self.config.latex_elements:
self.elements['fontpkg'] = ''
elif self.elements['polyglossia']:
self.elements['classoptions'] += ',' + self.babel.get_language()
@@ -612,25 +587,15 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.elements['multilingual'] = '%s\n%s' % (self.elements['polyglossia'],
mainlanguage)
- if getattr(builder, 'usepackages', None):
- def declare_package(packagename, options=None):
- # type:(unicode, unicode) -> unicode
- if options:
- return '\\usepackage[%s]{%s}' % (options, packagename)
- else:
- return '\\usepackage{%s}' % (packagename,)
- usepackages = (declare_package(*p) for p in builder.usepackages)
- self.elements['usepackages'] += "\n".join(usepackages)
-
minsecnumdepth = self.secnumdepth # 2 from legacy sphinx manual/howto
- if document.get('tocdepth'):
+ if self.document.get('tocdepth'):
# reduce tocdepth if `part` or `chapter` is used for top_sectionlevel
# tocdepth = -1: show only parts
# tocdepth = 0: show parts and chapters
# tocdepth = 1: show parts, chapters and sections
# tocdepth = 2: show parts, chapters, sections and subsections
# ...
- tocdepth = document['tocdepth'] + self.top_sectionlevel - 2
+ tocdepth = self.document['tocdepth'] + self.top_sectionlevel - 2
if len(self.sectionnames) < len(LATEXSECTIONNAMES) and \
self.top_sectionlevel > 0:
tocdepth += 1 # because top_sectionlevel is shifted by -1
@@ -641,16 +606,17 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.elements['tocdepth'] = '\\setcounter{tocdepth}{%d}' % tocdepth
minsecnumdepth = max(minsecnumdepth, tocdepth)
- if builder.config.numfig and (builder.config.numfig_secnum_depth > 0):
+ if self.config.numfig and (self.config.numfig_secnum_depth > 0):
minsecnumdepth = max(minsecnumdepth, self.numfig_secnum_depth - 1)
if minsecnumdepth > self.secnumdepth:
self.elements['secnumdepth'] = '\\setcounter{secnumdepth}{%d}' %\
minsecnumdepth
- if getattr(document.settings, 'contentsname', None):
- self.elements['contentsname'] = \
- self.babel_renewcommand('\\contentsname', document.settings.contentsname)
+ contentsname = self.settings.contentsname
+ if contentsname:
+ self.elements['contentsname'] = self.babel_renewcommand('\\contentsname',
+ contentsname)
if self.elements['maxlistdepth']:
self.elements['sphinxpkgoptions'] += (',maxlistdepth=%s' %
@@ -664,72 +630,52 @@ class LaTeXTranslator(nodes.NodeVisitor):
if self.elements['extraclassoptions']:
self.elements['classoptions'] += ',' + \
self.elements['extraclassoptions']
- self.elements['translatablestrings'] = (
- self.babel_renewcommand(
- '\\literalblockcontinuedname', self.encode(_('continued from previous page'))
- ) +
- self.babel_renewcommand(
- '\\literalblockcontinuesname', self.encode(_('continues on next page'))
- ) +
- self.babel_renewcommand(
- '\\sphinxnonalphabeticalgroupname', self.encode(_('Non-alphabetical'))
- ) +
- self.babel_renewcommand(
- '\\sphinxsymbolsname', self.encode(_('Symbols'))
- ) +
- self.babel_renewcommand(
- '\\sphinxnumbersname', self.encode(_('Numbers'))
- )
- )
- self.elements['pageautorefname'] = \
- self.babel_defmacro('\\pageautorefname', self.encode(_('page')))
- self.elements['numfig_format'] = self.generate_numfig_format(builder)
-
- self.highlighter = highlighting.PygmentsBridge('latex', builder.config.pygments_style)
- self.context = [] # type: List[Any]
- self.descstack = [] # type: List[unicode]
- self.table = None # type: Table
- self.next_table_colspec = None # type: unicode
- self.bodystack = [] # type: List[List[unicode]]
- self.footnote_restricted = False
- self.pending_footnotes = [] # type: List[nodes.footnote_reference]
- self.curfilestack = [] # type: List[unicode]
- self.handled_abbrs = set() # type: Set[unicode]
+
+ self.highlighter = highlighting.PygmentsBridge('latex', self.config.pygments_style)
+ self.context = [] # type: List[Any]
+ self.descstack = [] # type: List[str]
+ self.table = None # type: Table
+ self.next_table_colspec = None # type: str
+ self.bodystack = [] # type: List[List[str]]
+ self.footnote_restricted = None # type: nodes.Element
+ self.pending_footnotes = [] # type: List[nodes.footnote_reference]
+ self.curfilestack = [] # type: List[str]
+ self.handled_abbrs = set() # type: Set[str]
def pushbody(self, newbody):
- # type: (List[unicode]) -> None
+ # type: (List[str]) -> None
self.bodystack.append(self.body)
self.body = newbody
def popbody(self):
- # type: () -> List[unicode]
+ # type: () -> List[str]
body = self.body
self.body = self.bodystack.pop()
return body
def restrict_footnote(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
warnings.warn('LaTeXWriter.restrict_footnote() is deprecated.',
RemovedInSphinx30Warning, stacklevel=2)
- if self.footnote_restricted is False:
+ if self.footnote_restricted is None:
self.footnote_restricted = node
self.pending_footnotes = []
def unrestrict_footnote(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
warnings.warn('LaTeXWriter.unrestrict_footnote() is deprecated.',
RemovedInSphinx30Warning, stacklevel=2)
if self.footnote_restricted == node:
- self.footnote_restricted = False
+ self.footnote_restricted = None
for footnode in self.pending_footnotes:
footnode['footnotetext'] = True
footnode.walkabout(self)
self.pending_footnotes = []
def format_docclass(self, docclass):
- # type: (unicode) -> unicode
+ # type: (str) -> str
""" prepends prefix to sphinx document classes
"""
if docclass in self.docclasses:
@@ -737,22 +683,22 @@ class LaTeXTranslator(nodes.NodeVisitor):
return docclass
def astext(self):
- # type: () -> unicode
+ # type: () -> str
self.elements.update({
- 'body': u''.join(self.body),
+ 'body': ''.join(self.body),
'indices': self.generate_indices()
})
return self.render('latex.tex_t', self.elements)
def hypertarget(self, id, withdoc=True, anchor=True):
- # type: (unicode, bool, bool) -> unicode
+ # type: (str, bool, bool) -> str
if withdoc:
id = self.curfilestack[-1] + ':' + id
return (anchor and '\\phantomsection' or '') + \
'\\label{%s}' % self.idescape(id)
def hypertarget_to(self, node, anchor=False):
- # type: (nodes.Node, bool) -> unicode
+ # type: (nodes.Element, bool) -> str
labels = ''.join(self.hypertarget(node_id, anchor=False) for node_id in node['ids'])
if anchor:
return r'\phantomsection' + labels
@@ -760,21 +706,21 @@ class LaTeXTranslator(nodes.NodeVisitor):
return labels
def hyperlink(self, id):
- # type: (unicode) -> unicode
+ # type: (str) -> str
return '{\\hyperref[%s]{' % self.idescape(id)
def hyperpageref(self, id):
- # type: (unicode) -> unicode
+ # type: (str) -> str
return '\\autopageref*{%s}' % self.idescape(id)
def idescape(self, id):
- # type: (unicode) -> unicode
- return '\\detokenize{%s}' % text_type(id).translate(tex_replace_map).\
+ # type: (str) -> str
+ return '\\detokenize{%s}' % str(id).translate(tex_replace_map).\
encode('ascii', 'backslashreplace').decode('ascii').\
replace('\\', '_')
def babel_renewcommand(self, command, definition):
- # type: (unicode, unicode) -> unicode
+ # type: (str, str) -> str
if self.elements['multilingual']:
prefix = '\\addto\\captions%s{' % self.babel.get_language()
suffix = '}'
@@ -784,66 +730,17 @@ class LaTeXTranslator(nodes.NodeVisitor):
return ('%s\\renewcommand{%s}{%s}%s\n' % (prefix, command, definition, suffix))
- def babel_defmacro(self, name, definition):
- # type: (unicode, unicode) -> unicode
- if self.elements['babel']:
- prefix = '\\addto\\extras%s{' % self.babel.get_language()
- suffix = '}'
- else: # babel is disabled (mainly for Japanese environment)
- prefix = ''
- suffix = ''
-
- return ('%s\\def%s{%s}%s\n' % (prefix, name, definition, suffix))
-
- def generate_numfig_format(self, builder):
- # type: (Builder) -> unicode
- ret = [] # type: List[unicode]
- figure = self.builder.config.numfig_format['figure'].split('%s', 1)
- if len(figure) == 1:
- ret.append('\\def\\fnum@figure{%s}\n' %
- text_type(figure[0]).strip().translate(tex_escape_map))
- else:
- definition = escape_abbr(text_type(figure[0]).translate(tex_escape_map))
- ret.append(self.babel_renewcommand('\\figurename', definition))
- ret.append('\\makeatletter\n')
- ret.append('\\def\\fnum@figure{\\figurename\\thefigure{}%s}\n' %
- text_type(figure[1]).translate(tex_escape_map))
- ret.append('\\makeatother\n')
-
- table = self.builder.config.numfig_format['table'].split('%s', 1)
- if len(table) == 1:
- ret.append('\\def\\fnum@table{%s}\n' %
- text_type(table[0]).strip().translate(tex_escape_map))
- else:
- definition = escape_abbr(text_type(table[0]).translate(tex_escape_map))
- ret.append(self.babel_renewcommand('\\tablename', definition))
- ret.append('\\makeatletter\n')
- ret.append('\\def\\fnum@table{\\tablename\\thetable{}%s}\n' %
- text_type(table[1]).translate(tex_escape_map))
- ret.append('\\makeatother\n')
-
- codeblock = self.builder.config.numfig_format['code-block'].split('%s', 1)
- if len(codeblock) == 1:
- pass # FIXME
- else:
- definition = text_type(codeblock[0]).strip().translate(tex_escape_map)
- ret.append(self.babel_renewcommand('\\literalblockname', definition))
- if codeblock[1]:
- pass # FIXME
-
- return ''.join(ret)
-
def generate_indices(self):
- # type: (Builder) -> unicode
+ # type: () -> str
def generate(content, collapsed):
- # type: (List[Tuple[unicode, List[Tuple[unicode, unicode, unicode, unicode, unicode]]]], bool) -> None # NOQA
+ # type: (List[Tuple[str, List[IndexEntry]]], bool) -> None
ret.append('\\begin{sphinxtheindex}\n')
ret.append('\\let\\bigletter\\sphinxstyleindexlettergroup\n')
for i, (letter, entries) in enumerate(content):
if i > 0:
ret.append('\\indexspace\n')
ret.append('\\bigletter{%s}\n' %
- text_type(letter).translate(tex_escape_map))
+ str(letter).translate(tex_escape_map))
for entry in entries:
if not entry[3]:
continue
@@ -860,7 +757,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
# latex_domain_indices can be False/True or a list of index names
indices_config = self.builder.config.latex_domain_indices
if indices_config:
- for domain in itervalues(self.builder.env.domains):
+ for domain in self.builder.env.domains.values():
for indexcls in domain.indices:
indexname = '%s-%s' % (domain.name, indexcls.name)
if isinstance(indices_config, list):
@@ -870,14 +767,14 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.builder.docnames)
if not content:
continue
- ret.append(u'\\renewcommand{\\indexname}{%s}\n' %
+ ret.append('\\renewcommand{\\indexname}{%s}\n' %
indexcls.localname)
generate(content, collapsed)
return ''.join(ret)
def render(self, template_name, variables):
- # type: (unicode, Dict) -> unicode
+ # type: (str, Dict) -> str
for template_dir in self.builder.config.templates_path:
template = path.join(self.builder.confdir, template_dir,
template_name)
@@ -887,14 +784,14 @@ class LaTeXTranslator(nodes.NodeVisitor):
return LaTeXRenderer().render(template_name, variables)
def visit_document(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.curfilestack.append(node.get('docname', ''))
if self.first_document == 1:
# the first document is all the regular content ...
self.first_document = 0
elif self.first_document == 0:
# ... and all others are the appendices
- self.body.append(u'\n\\appendix\n')
+ self.body.append('\n\\appendix\n')
self.first_document = -1
if 'docname' in node:
self.body.append(self.hypertarget(':doc'))
@@ -902,88 +799,68 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.sectionlevel = self.top_sectionlevel - 1
def depart_document(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
def visit_start_of_file(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.curfilestack.append(node['docname'])
- def collect_footnotes(self, node):
- # type: (nodes.Node) -> Dict[unicode, List[Union[collected_footnote, bool]]]
- def footnotes_under(n):
- # type: (nodes.Node) -> Iterator[nodes.Node]
- if isinstance(n, nodes.footnote):
- yield n
- else:
- for c in n.children:
- if isinstance(c, addnodes.start_of_file):
- continue
- for k in footnotes_under(c):
- yield k
-
- fnotes = {} # type: Dict[unicode, List[Union[collected_footnote, bool]]]
- for fn in footnotes_under(node):
- num = fn.children[0].astext().strip()
- newnode = collected_footnote(*fn.children, number=num)
- fnotes[num] = [newnode, False]
- return fnotes
-
def depart_start_of_file(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.curfilestack.pop()
def visit_section(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
if not self.this_is_the_title:
self.sectionlevel += 1
self.body.append('\n\n')
def depart_section(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.sectionlevel = max(self.sectionlevel - 1,
self.top_sectionlevel - 1)
def visit_problematic(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append(r'{\color{red}\bfseries{}')
def depart_problematic(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append('}')
def visit_topic(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.in_minipage = 1
self.body.append('\n\\begin{sphinxShadowBox}\n')
def depart_topic(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.in_minipage = 0
self.body.append('\\end{sphinxShadowBox}\n')
visit_sidebar = visit_topic
depart_sidebar = depart_topic
def visit_glossary(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
def depart_glossary(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
def visit_productionlist(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append('\n\n\\begin{productionlist}\n')
self.in_production_list = 1
def depart_productionlist(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append('\\end{productionlist}\n\n')
self.in_production_list = 0
def visit_production(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
if node['tokenname']:
tn = node['tokenname']
self.body.append(self.hypertarget('grammar-token-' + tn))
@@ -992,19 +869,19 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.body.append('\\productioncont{')
def depart_production(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append('}\n')
def visit_transition(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append(self.elements['transition'])
def depart_transition(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
def visit_title(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
parent = node.parent
if isinstance(parent, addnodes.seealso):
# the environment already handles this
@@ -1025,7 +902,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
short = ''
if node.traverse(nodes.image):
short = ('[%s]' %
- u' '.join(clean_astext(node).split()).translate(tex_escape_map))
+ ' '.join(clean_astext(node).split()).translate(tex_escape_map))
try:
self.body.append(r'\%s%s{' % (self.sectionnames[self.sectionlevel], short))
@@ -1054,7 +931,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.in_title = 1
def depart_title(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.in_title = 0
if isinstance(node.parent, nodes.table):
self.table.caption = self.popbody()
@@ -1062,7 +939,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.body.append(self.context.pop())
def visit_subtitle(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
if isinstance(node.parent, nodes.sidebar):
self.body.append('\\sphinxstylesidebarsubtitle{')
self.context.append('}\n')
@@ -1070,21 +947,21 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.context.append('')
def depart_subtitle(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append(self.context.pop())
def visit_desc(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append('\n\n\\begin{fulllineitems}\n')
if self.table:
self.table.has_problematic = True
def depart_desc(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append('\n\\end{fulllineitems}\n\n')
def _visit_signature_line(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
for child in node:
if isinstance(child, addnodes.desc_parameterlist):
self.body.append(r'\pysiglinewithargsret{')
@@ -1093,11 +970,11 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.body.append(r'\pysigline{')
def _depart_signature_line(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append('}')
def visit_desc_signature(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
if node.parent['objtype'] != 'describe' and node['ids']:
hyper = self.hypertarget(node['ids'][0])
else:
@@ -1109,71 +986,71 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.body.append('%\n\\pysigstartmultiline\n')
def depart_desc_signature(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
if not node.get('is_multiline'):
self._depart_signature_line(node)
else:
self.body.append('%\n\\pysigstopmultiline')
def visit_desc_signature_line(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self._visit_signature_line(node)
def depart_desc_signature_line(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self._depart_signature_line(node)
def visit_desc_addname(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append(r'\sphinxcode{\sphinxupquote{')
self.literal_whitespace += 1
def depart_desc_addname(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append('}}')
self.literal_whitespace -= 1
def visit_desc_type(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
def depart_desc_type(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
def visit_desc_returns(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append(r'{ $\rightarrow$ ')
def depart_desc_returns(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append(r'}')
def visit_desc_name(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append(r'\sphinxbfcode{\sphinxupquote{')
self.no_contractions += 1
self.literal_whitespace += 1
def depart_desc_name(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append('}}')
self.literal_whitespace -= 1
self.no_contractions -= 1
def visit_desc_parameterlist(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
# close name, open parameterlist
self.body.append('}{')
self.first_param = 1
def depart_desc_parameterlist(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
# close parameterlist, open return annotation
self.body.append('}{')
def visit_desc_parameter(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
if not self.first_param:
self.body.append(', ')
else:
@@ -1182,69 +1059,69 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.body.append(r'\emph{')
def depart_desc_parameter(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
if not node.hasattr('noemph'):
self.body.append('}')
def visit_desc_optional(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append(r'\sphinxoptional{')
def depart_desc_optional(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append('}')
def visit_desc_annotation(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append(r'\sphinxbfcode{\sphinxupquote{')
def depart_desc_annotation(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append('}}')
def visit_desc_content(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
if node.children and not isinstance(node.children[0], nodes.paragraph):
# avoid empty desc environment which causes a formatting bug
self.body.append('~')
def depart_desc_content(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
def visit_seealso(self, node):
- # type: (nodes.Node) -> None
- self.body.append(u'\n\n\\sphinxstrong{%s:}\n\n' % admonitionlabels['seealso'])
+ # type: (nodes.Element) -> None
+ self.body.append('\n\n\\sphinxstrong{%s:}\n\n' % admonitionlabels['seealso'])
def depart_seealso(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append("\n\n")
def visit_rubric(self, node):
- # type: (nodes.Node) -> None
- if len(node.children) == 1 and node.children[0].astext() in \
- ('Footnotes', _('Footnotes')):
+ # type: (nodes.Element) -> None
+ if len(node) == 1 and node.astext() in ('Footnotes', _('Footnotes')):
raise nodes.SkipNode
self.body.append('\\subsubsection*{')
self.context.append('}\n')
self.in_title = 1
def depart_rubric(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.in_title = 0
self.body.append(self.context.pop())
def visit_footnote(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.in_footnote += 1
+ label = cast(nodes.label, node[0])
if self.in_parsed_literal:
- self.body.append('\\begin{footnote}[%s]' % node[0].astext())
+ self.body.append('\\begin{footnote}[%s]' % label.astext())
else:
- self.body.append('%%\n\\begin{footnote}[%s]' % node[0].astext())
+ self.body.append('%%\n\\begin{footnote}[%s]' % label.astext())
self.body.append('\\sphinxAtStartFootnote\n')
def depart_footnote(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
if self.in_parsed_literal:
self.body.append('\\end{footnote}')
else:
@@ -1252,16 +1129,16 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.in_footnote -= 1
def visit_label(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
raise nodes.SkipNode
def visit_tabular_col_spec(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.next_table_colspec = node['spec']
raise nodes.SkipNode
def visit_table(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
if self.table:
raise UnsupportedError(
'%s:%s: nested tables are not yet implemented.' %
@@ -1270,12 +1147,12 @@ class LaTeXTranslator(nodes.NodeVisitor):
if self.next_table_colspec:
self.table.colspec = '{%s}\n' % self.next_table_colspec
if 'colwidths-given' in node.get('classes', []):
- logger.info('both tabularcolumns and :widths: option are given. '
- ':widths: is ignored.', location=node)
+ logger.info(__('both tabularcolumns and :widths: option are given. '
+ ':widths: is ignored.'), location=node)
self.next_table_colspec = None
def depart_table(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
labels = self.hypertarget_to(node)
table_type = self.table.get_table_type()
table = self.render(table_type + '.tex_t',
@@ -1287,7 +1164,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.table = None
def visit_colspec(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.table.colcount += 1
if 'colwidth' in node:
self.table.colwidths.append(node['colwidth'])
@@ -1295,37 +1172,37 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.table.stubs.append(self.table.colcount - 1)
def depart_colspec(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
def visit_tgroup(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
def depart_tgroup(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
def visit_thead(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
# Redirect head output until header is finished.
self.pushbody(self.table.header)
def depart_thead(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.popbody()
def visit_tbody(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
# Redirect body output until table is finished.
self.pushbody(self.table.body)
def depart_tbody(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.popbody()
def visit_row(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.table.col = 0
# fill columns if the row starts with the bottom of multirow cell
@@ -1346,7 +1223,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
(cell.width, cell.cell_id))
def depart_row(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append('\\\\\n')
cells = [self.table.cell(self.table.row, i) for i in range(self.table.colcount)]
underlined = [cell.row + cell.height == self.table.row + 1 for cell in cells]
@@ -1364,7 +1241,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.table.row += 1
def visit_entry(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
if self.table.col > 0:
self.body.append('&')
self.table.add_cell(node.get('morerows', 0) + 1, node.get('morecols', 0) + 1)
@@ -1402,7 +1279,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.context.append(context)
def depart_entry(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
if self.needs_linetrimming:
self.needs_linetrimming = 0
body = self.popbody()
@@ -1436,31 +1313,32 @@ class LaTeXTranslator(nodes.NodeVisitor):
(nextcell.width, nextcell.cell_id))
def visit_acks(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
# this is a list in the source, but should be rendered as a
# comma-separated list here
+ bullet_list = cast(nodes.bullet_list, node[0])
+ list_items = cast(Iterable[nodes.list_item], bullet_list)
self.body.append('\n\n')
- self.body.append(', '.join(n.astext()
- for n in node.children[0].children) + '.')
+ self.body.append(', '.join(n.astext() for n in list_items) + '.')
self.body.append('\n\n')
raise nodes.SkipNode
def visit_bullet_list(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
if not self.compact_list:
self.body.append('\\begin{itemize}\n')
if self.table:
self.table.has_problematic = True
def depart_bullet_list(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
if not self.compact_list:
self.body.append('\\end{itemize}\n')
def visit_enumerated_list(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
def get_enumtype(node):
- # type: (nodes.Node) -> unicode
+ # type: (nodes.Element) -> str
enumtype = node.get('enumtype', 'arabic')
if 'alpha' in enumtype and 26 < node.get('start', 0) + len(node):
# fallback to arabic if alphabet counter overflows
@@ -1469,7 +1347,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
return enumtype
def get_nested_level(node):
- # type: (nodes.Node) -> int
+ # type: (nodes.Element) -> int
if node is None:
return 0
elif isinstance(node, nodes.enumerated_list):
@@ -1495,41 +1373,41 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.table.has_problematic = True
def depart_enumerated_list(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append('\\end{enumerate}\n')
def visit_list_item(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
# Append "{}" in case the next character is "[", which would break
# LaTeX's list environment (no numbering and the "[" is not printed).
self.body.append(r'\item {} ')
def depart_list_item(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append('\n')
def visit_definition_list(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append('\\begin{description}\n')
if self.table:
self.table.has_problematic = True
def depart_definition_list(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append('\\end{description}\n')
def visit_definition_list_item(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
def depart_definition_list_item(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
def visit_term(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.in_term += 1
- ctx = '' # type: unicode
+ ctx = ''
if node.get('ids'):
ctx = '\\phantomsection'
for node_id in node['ids']:
@@ -1539,42 +1417,42 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.context.append(ctx)
def depart_term(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append(self.context.pop())
self.in_term -= 1
def visit_classifier(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append('{[}')
def depart_classifier(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append('{]}')
def visit_definition(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
def depart_definition(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append('\n')
def visit_field_list(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append('\\begin{quote}\\begin{description}\n')
if self.table:
self.table.has_problematic = True
def depart_field_list(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append('\\end{description}\\end{quote}\n')
def visit_field(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
def depart_field(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
visit_field_name = visit_term
@@ -1584,7 +1462,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
depart_field_body = depart_definition
def visit_paragraph(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
index = node.parent.index(node)
if (index > 0 and isinstance(node.parent, nodes.compound) and
not isinstance(node.parent[index - 1], nodes.paragraph) and
@@ -1599,21 +1477,21 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.body.append('\n')
def depart_paragraph(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append('\n')
def visit_centered(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append('\n\\begin{center}')
if self.table:
self.table.has_problematic = True
def depart_centered(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append('\n\\end{center}')
def visit_hlist(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
# for now, we don't support a more compact list format
# don't add individual itemize environments, but one for all columns
self.compact_list += 1
@@ -1623,20 +1501,20 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.table.has_problematic = True
def depart_hlist(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.compact_list -= 1
self.body.append('\\end{itemize}\n')
def visit_hlistcol(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
def depart_hlistcol(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
def latex_image_length(self, width_str, scale = 100):
- # type: (nodes.Node, int) -> unicode
+ # type: (str, int) -> str
try:
return rstdim_to_latexdim(width_str, scale)
except ValueError:
@@ -1644,16 +1522,16 @@ class LaTeXTranslator(nodes.NodeVisitor):
return None
def is_inline(self, node):
- # type: (nodes.Node) -> bool
+ # type: (nodes.Element) -> bool
"""Check whether a node represents an inline element."""
return isinstance(node.parent, nodes.TextElement)
def visit_image(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
attrs = node.attributes
- pre = [] # type: List[unicode]
+ pre = [] # type: List[str]
# in reverse order
- post = [] # type: List[unicode]
+ post = [] # type: List[str]
include_graphics_options = []
is_inline = self.is_inline(node)
if 'width' in attrs:
@@ -1727,11 +1605,11 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.body.extend(post)
def depart_image(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
def visit_figure(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
if self.table:
# TODO: support align option
if 'width' in node:
@@ -1748,7 +1626,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
length = None
if 'width' in node:
length = self.latex_image_length(node['width'])
- elif 'width' in node[0]:
+ elif isinstance(node[0], nodes.image) and 'width' in node[0]:
length = self.latex_image_length(node[0]['width'])
self.body.append('\\begin{wrapfigure}{%s}{%s}\n\\centering' %
(node['align'] == 'right' and 'r' or 'l', length or '0pt'))
@@ -1764,11 +1642,11 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.context.append('\\end{figure}\n')
def depart_figure(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append(self.context.pop())
def visit_caption(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.in_caption += 1
if isinstance(node.parent, captioned_literal_block):
self.body.append('\\sphinxSetupCaptionForVerbatim{')
@@ -1780,7 +1658,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.body.append('\\caption{')
def depart_caption(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append('}')
if isinstance(node.parent, nodes.figure):
labels = self.hypertarget_to(node.parent)
@@ -1788,64 +1666,62 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.in_caption -= 1
def visit_legend(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append('\n\\begin{sphinxlegend}')
def depart_legend(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append('\\end{sphinxlegend}\n')
def visit_admonition(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append('\n\\begin{sphinxadmonition}{note}')
def depart_admonition(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append('\\end{sphinxadmonition}\n')
- def _make_visit_admonition(name):
- # type: (unicode) -> Callable[[LaTeXTranslator, nodes.Node], None]
- def visit_admonition(self, node):
- # type: (nodes.Node) -> None
- self.body.append(u'\n\\begin{sphinxadmonition}{%s}{%s:}' %
- (name, admonitionlabels[name]))
- return visit_admonition
+ def _visit_named_admonition(self, node):
+ # type: (nodes.Element) -> None
+ label = admonitionlabels[node.tagname]
+ self.body.append('\n\\begin{sphinxadmonition}{%s}{%s:}' %
+ (node.tagname, label))
def _depart_named_admonition(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append('\\end{sphinxadmonition}\n')
- visit_attention = _make_visit_admonition('attention')
+ visit_attention = _visit_named_admonition
depart_attention = _depart_named_admonition
- visit_caution = _make_visit_admonition('caution')
+ visit_caution = _visit_named_admonition
depart_caution = _depart_named_admonition
- visit_danger = _make_visit_admonition('danger')
+ visit_danger = _visit_named_admonition
depart_danger = _depart_named_admonition
- visit_error = _make_visit_admonition('error')
+ visit_error = _visit_named_admonition
depart_error = _depart_named_admonition
- visit_hint = _make_visit_admonition('hint')
+ visit_hint = _visit_named_admonition
depart_hint = _depart_named_admonition
- visit_important = _make_visit_admonition('important')
+ visit_important = _visit_named_admonition
depart_important = _depart_named_admonition
- visit_note = _make_visit_admonition('note')
+ visit_note = _visit_named_admonition
depart_note = _depart_named_admonition
- visit_tip = _make_visit_admonition('tip')
+ visit_tip = _visit_named_admonition
depart_tip = _depart_named_admonition
- visit_warning = _make_visit_admonition('warning')
+ visit_warning = _visit_named_admonition
depart_warning = _depart_named_admonition
def visit_versionmodified(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
def depart_versionmodified(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
def visit_target(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
def add_target(id):
- # type: (unicode) -> None
+ # type: (str) -> None
# indexing uses standard LaTeX index markup, so the targets
# will be generated differently
if id.startswith('index-'):
@@ -1866,11 +1742,11 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.body.append(self.hypertarget(id, anchor=anchor))
# skip if visitor for next node supports hyperlink
- next_node = node
+ next_node = node # type: nodes.Node
while isinstance(next_node, nodes.target):
next_node = next_node.next_node(ascend=True)
- domain = self.builder.env.get_domain('std')
+ domain = cast(StandardDomain, self.builder.env.get_domain('std'))
if isinstance(next_node, HYPERLINK_SUPPORT_NODES):
return
elif domain.get_enumerable_node_type(next_node) and domain.get_numfig_title(next_node):
@@ -1884,20 +1760,20 @@ class LaTeXTranslator(nodes.NodeVisitor):
add_target(id)
def depart_target(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
def visit_attribution(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append('\n\\begin{flushright}\n')
self.body.append('---')
def depart_attribution(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append('\n\\end{flushright}\n')
def visit_index(self, node, scre = None):
- # type: (nodes.Node, None) -> None
+ # type: (nodes.Element, Pattern) -> None
def escape(value):
value = self.encode(value)
value = value.replace(r'\{', r'\sphinxleftcurlybrace{}')
@@ -1967,7 +1843,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
raise nodes.SkipNode
def visit_raw(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
if not self.is_inline(node):
self.body.append('\n')
if 'latex' in node.get('format', '').split():
@@ -1977,7 +1853,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
raise nodes.SkipNode
def visit_reference(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
if not self.in_title:
for id in node.get('ids'):
anchor = not self.in_caption
@@ -2007,8 +1883,9 @@ class LaTeXTranslator(nodes.NodeVisitor):
# reference to a label
id = uri[1:].replace('#', ':')
self.body.append(self.hyperlink(id))
- if len(node) and hasattr(node[0], 'attributes') and \
- 'std-term' in node[0].get('classes', []):
+ if (len(node) and
+ isinstance(node[0], nodes.Element) and
+ 'std-term' in node[0].get('classes', [])):
# don't add a pageref for glossary terms
self.context.append('}}}')
# mark up as termreference
@@ -2032,18 +1909,18 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.context.append('}')
def depart_reference(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append(self.context.pop())
def visit_number_reference(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
if node.get('refid'):
id = self.curfilestack[-1] + ':' + node['refid']
else:
id = node.get('refuri', '')[1:].replace('#', ':')
title = node.get('title', '%s')
- title = text_type(title).translate(tex_escape_map).replace('\\%s', '%s')
+ title = str(title).translate(tex_escape_map).replace('\\%s', '%s')
if '\\{name\\}' in title or '\\{number\\}' in title:
# new style format (cf. "Fig.%{number}")
title = title.replace('\\{name\\}', '{name}').replace('\\{number\\}', '{number}')
@@ -2058,59 +1935,59 @@ class LaTeXTranslator(nodes.NodeVisitor):
raise nodes.SkipNode
def visit_download_reference(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
def depart_download_reference(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
def visit_pending_xref(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
def depart_pending_xref(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
def visit_emphasis(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append(r'\sphinxstyleemphasis{')
def depart_emphasis(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append('}')
def visit_literal_emphasis(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append(r'\sphinxstyleliteralemphasis{\sphinxupquote{')
self.no_contractions += 1
def depart_literal_emphasis(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append('}}')
self.no_contractions -= 1
def visit_strong(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append(r'\sphinxstylestrong{')
def depart_strong(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append('}')
def visit_literal_strong(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append(r'\sphinxstyleliteralstrong{\sphinxupquote{')
self.no_contractions += 1
def depart_literal_strong(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append('}}')
self.no_contractions -= 1
def visit_abbreviation(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
abbr = node.astext()
self.body.append(r'\sphinxstyleabbreviation{')
# spell out the explanation once
@@ -2121,51 +1998,53 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.context.append('}')
def depart_abbreviation(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append(self.context.pop())
def visit_manpage(self, node):
- # type: (nodes.Node) -> Any
+ # type: (nodes.Element) -> None
return self.visit_literal_emphasis(node)
def depart_manpage(self, node):
- # type: (nodes.Node) -> Any
+ # type: (nodes.Element) -> None
return self.depart_literal_emphasis(node)
def visit_title_reference(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append(r'\sphinxtitleref{')
def depart_title_reference(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append('}')
def visit_thebibliography(self, node):
- # type: (nodes.Node) -> None
- longest_label = max((subnode[0].astext() for subnode in node), key=len)
+ # type: (thebibliography) -> None
+ citations = cast(Iterable[nodes.citation], node)
+ labels = (cast(nodes.label, citation[0]) for citation in citations)
+ longest_label = max((label.astext() for label in labels), key=len)
if len(longest_label) > MAX_CITATION_LABEL_LENGTH:
# adjust max width of citation labels not to break the layout
longest_label = longest_label[:MAX_CITATION_LABEL_LENGTH]
- self.body.append(u'\n\\begin{sphinxthebibliography}{%s}\n' %
+ self.body.append('\n\\begin{sphinxthebibliography}{%s}\n' %
self.encode(longest_label))
def depart_thebibliography(self, node):
- # type: (nodes.Node) -> None
- self.body.append(u'\\end{sphinxthebibliography}\n')
+ # type: (thebibliography) -> None
+ self.body.append('\\end{sphinxthebibliography}\n')
def visit_citation(self, node):
- # type: (nodes.Node) -> None
- label = node[0].astext()
- self.body.append(u'\\bibitem[%s]{%s:%s}' %
- (self.encode(label), node['docname'], node['ids'][0]))
+ # type: (nodes.Element) -> None
+ label = cast(nodes.label, node[0])
+ self.body.append('\\bibitem[%s]{%s:%s}' % (self.encode(label.astext()),
+ node['docname'], node['ids'][0]))
def depart_citation(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
def visit_citation_reference(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
if self.in_title:
pass
else:
@@ -2173,11 +2052,11 @@ class LaTeXTranslator(nodes.NodeVisitor):
raise nodes.SkipNode
def depart_citation_reference(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
def visit_literal(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.no_contractions += 1
if self.in_title:
self.body.append(r'\sphinxstyleliteralintitle{\sphinxupquote{')
@@ -2185,43 +2064,43 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.body.append(r'\sphinxcode{\sphinxupquote{')
def depart_literal(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.no_contractions -= 1
self.body.append('}}')
def visit_footnote_reference(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
raise nodes.SkipNode
def visit_footnotemark(self, node):
- # type: (nodes.Node) -> None
+ # type: (footnotemark) -> None
self.body.append('\\sphinxfootnotemark[')
def depart_footnotemark(self, node):
- # type: (nodes.Node) -> None
+ # type: (footnotemark) -> None
self.body.append(']')
def visit_footnotetext(self, node):
- # type: (nodes.Node) -> None
- number = node[0].astext()
+ # type: (footnotetext) -> None
+ label = cast(nodes.label, node[0])
self.body.append('%%\n\\begin{footnotetext}[%s]'
- '\\sphinxAtStartFootnote\n' % number)
+ '\\sphinxAtStartFootnote\n' % label.astext())
def depart_footnotetext(self, node):
- # type: (nodes.Node) -> None
+ # type: (footnotetext) -> None
# the \ignorespaces in particular for after table header use
self.body.append('%\n\\end{footnotetext}\\ignorespaces ')
def visit_captioned_literal_block(self, node):
- # type: (nodes.Node) -> None
+ # type: (captioned_literal_block) -> None
pass
def depart_captioned_literal_block(self, node):
- # type: (nodes.Node) -> None
+ # type: (captioned_literal_block) -> None
pass
def visit_literal_block(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
if node.rawsource != node.astext():
# most probably a parsed-literal block -- don't highlight
self.in_parsed_literal += 1
@@ -2248,7 +2127,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
location=(self.curfilestack[-1], node.line), **highlight_args
)
# workaround for Unicode issue
- hlcode = hlcode.replace(u'€', u'@texteuro[]')
+ hlcode = hlcode.replace('€', '@texteuro[]')
if self.in_footnote:
self.body.append('\n\\sphinxSetupCodeBlockInFootnote')
hlcode = hlcode.replace('\\begin{Verbatim}',
@@ -2279,22 +2158,22 @@ class LaTeXTranslator(nodes.NodeVisitor):
raise nodes.SkipNode
def depart_literal_block(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append('\n\\end{sphinxalltt}\n')
self.in_parsed_literal -= 1
visit_doctest_block = visit_literal_block
depart_doctest_block = depart_literal_block
def visit_line(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append('\\item[] ')
def depart_line(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append('\n')
def visit_line_block(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
if isinstance(node.parent, nodes.line_block):
self.body.append('\\item[]\n'
'\\begin{DUlineblock}{\\DUlineblockindent}\n')
@@ -2304,11 +2183,11 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.table.has_problematic = True
def depart_line_block(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append('\\end{DUlineblock}\n')
def visit_block_quote(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
# If the block quote contains a single object and that object
# is a list, then generate a list not a block quote.
# This lets us indent lists.
@@ -2324,7 +2203,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.table.has_problematic = True
def depart_block_quote(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
done = 0
if len(node.children) == 1:
child = node.children[0]
@@ -2337,56 +2216,56 @@ class LaTeXTranslator(nodes.NodeVisitor):
# option node handling copied from docutils' latex writer
def visit_option(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
if self.context[-1]:
# this is not the first option
self.body.append(', ')
def depart_option(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
# flag that the first option is done.
self.context[-1] += 1
def visit_option_argument(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
"""The delimiter betweeen an option and its argument."""
self.body.append(node.get('delimiter', ' '))
def depart_option_argument(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
def visit_option_group(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append('\\item [')
# flag for first option
self.context.append(0)
def depart_option_group(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.context.pop() # the flag
self.body.append('] ')
def visit_option_list(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append('\\begin{optionlist}{3cm}\n')
if self.table:
self.table.has_problematic = True
def depart_option_list(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append('\\end{optionlist}\n')
def visit_option_list_item(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
def depart_option_list_item(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
def visit_option_string(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
ostring = node.astext()
self.no_contractions += 1
self.body.append(self.encode(ostring))
@@ -2394,31 +2273,31 @@ class LaTeXTranslator(nodes.NodeVisitor):
raise nodes.SkipNode
def visit_description(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append(' ')
def depart_description(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
def visit_superscript(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append('$^{\\text{')
def depart_superscript(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append('}}$')
def visit_subscript(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append('$_{\\text{')
def depart_subscript(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append('}}$')
def visit_inline(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
classes = node.get('classes', [])
if classes in [['menuselection']]:
self.body.append(r'\sphinxmenuselection{')
@@ -2436,102 +2315,102 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.context.append('')
def depart_inline(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append(self.context.pop())
def visit_generated(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
def depart_generated(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
def visit_compound(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
def depart_compound(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
def visit_container(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
def depart_container(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
def visit_decoration(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
def depart_decoration(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
# docutils-generated elements that we don't support
def visit_header(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
raise nodes.SkipNode
def visit_footer(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
raise nodes.SkipNode
def visit_docinfo(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
raise nodes.SkipNode
# text handling
def encode(self, text):
- # type: (unicode) -> unicode
- text = text_type(text).translate(tex_escape_map)
+ # type: (str) -> str
+ text = str(text).translate(tex_escape_map)
if self.literal_whitespace:
# Insert a blank before the newline, to avoid
# ! LaTeX Error: There's no line here to end.
- text = text.replace(u'\n', u'~\\\\\n').replace(u' ', u'~')
+ text = text.replace('\n', '~\\\\\n').replace(' ', '~')
if self.no_contractions:
- text = text.replace('--', u'-{-}')
- text = text.replace("''", u"'{'}")
+ text = text.replace('--', '-{-}')
+ text = text.replace("''", "'{'}")
return text
def encode_uri(self, text):
- # type: (unicode) -> unicode
+ # type: (str) -> str
# in \href, the tilde is allowed and must be represented literally
return self.encode(text).replace('\\textasciitilde{}', '~')
def visit_Text(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Text) -> None
text = self.encode(node.astext())
self.body.append(text)
def depart_Text(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Text) -> None
pass
def visit_comment(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
raise nodes.SkipNode
def visit_meta(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
# only valid for HTML
raise nodes.SkipNode
def visit_system_message(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
def depart_system_message(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append('\n')
def visit_math(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
if self.in_title:
self.body.append(r'\protect\(%s\protect\)' % node.astext())
else:
@@ -2539,7 +2418,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
raise nodes.SkipNode
def visit_math_block(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
if node.get('label'):
label = "equation:%s:%s" % (node['docname'], node['label'])
else:
@@ -2556,7 +2435,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
raise nodes.SkipNode
def visit_math_reference(self, node):
- # type: (nodes.Node) -> None
+ # type: (math_reference) -> None
label = "equation:%s:%s" % (node['docname'], node['target'])
eqref_format = self.builder.config.math_eqref_format
if eqref_format:
@@ -2571,7 +2450,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.body.append(r'\eqref{%s}' % label)
def depart_math_reference(self, node):
- # type: (nodes.Node) -> None
+ # type: (math_reference) -> None
pass
def unknown_visit(self, node):
@@ -2580,16 +2459,40 @@ class LaTeXTranslator(nodes.NodeVisitor):
# --------- METHODS FOR COMPATIBILITY --------------------------------------
+ def collect_footnotes(self, node):
+ # type: (nodes.Element) -> Dict[str, List[Union[collected_footnote, bool]]]
+ def footnotes_under(n):
+ # type: (nodes.Element) -> Iterator[nodes.footnote]
+ if isinstance(n, nodes.footnote):
+ yield n
+ else:
+ for c in n.children:
+ if isinstance(c, addnodes.start_of_file):
+ continue
+ elif isinstance(c, nodes.Element):
+ yield from footnotes_under(c)
+
+ warnings.warn('LaTeXWriter.collected_footnote() is deprecated.',
+ RemovedInSphinx40Warning, stacklevel=2)
+
+ fnotes = {} # type: Dict[str, List[Union[collected_footnote, bool]]]
+ for fn in footnotes_under(node):
+ label = cast(nodes.label, fn[0])
+ num = label.astext().strip()
+ newnode = collected_footnote('', *fn.children, number=num)
+ fnotes[num] = [newnode, False]
+ return fnotes
+
@property
def footnotestack(self):
- # type: () -> List[Dict[unicode, List[Union[collected_footnote, bool]]]]
+ # type: () -> List[Dict[str, List[Union[collected_footnote, bool]]]]
warnings.warn('LaTeXWriter.footnotestack is deprecated.',
RemovedInSphinx30Warning, stacklevel=2)
return []
@property
def bibitems(self):
- # type: () -> List[List[unicode]]
+ # type: () -> List[List[str]]
warnings.warn('LaTeXTranslator.bibitems() is deprecated.',
RemovedInSphinx30Warning, stacklevel=2)
return []
@@ -2603,7 +2506,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
@property
def next_section_ids(self):
- # type: () -> Set[unicode]
+ # type: () -> Set[str]
warnings.warn('LaTeXTranslator.next_section_ids is deprecated.',
RemovedInSphinx30Warning, stacklevel=2)
return set()
@@ -2616,20 +2519,20 @@ class LaTeXTranslator(nodes.NodeVisitor):
return {}
def push_hyperlink_ids(self, figtype, ids):
- # type: (unicode, Set[unicode]) -> None
+ # type: (str, Set[str]) -> None
warnings.warn('LaTeXTranslator.push_hyperlink_ids() is deprecated.',
RemovedInSphinx30Warning, stacklevel=2)
pass
def pop_hyperlink_ids(self, figtype):
- # type: (unicode) -> Set[unicode]
+ # type: (str) -> Set[str]
warnings.warn('LaTeXTranslator.pop_hyperlink_ids() is deprecated.',
RemovedInSphinx30Warning, stacklevel=2)
return set()
@property
def hlsettingstack(self):
- # type: () -> List[List[Union[unicode, int]]]
+ # type: () -> List[List[Union[str, int]]]
warnings.warn('LaTeXTranslator.hlsettingstack is deprecated.',
RemovedInSphinx30Warning, stacklevel=2)
return [[self.builder.config.highlight_language, sys.maxsize]]
@@ -2644,12 +2547,88 @@ class LaTeXTranslator(nodes.NodeVisitor):
msg = __("Unknown configure key: latex_elements[%r] is ignored.")
logger.warning(msg % key)
+ def babel_defmacro(self, name, definition):
+ # type: (str, str) -> str
+ warnings.warn('babel_defmacro() is deprecated.',
+ RemovedInSphinx40Warning)
+
+ if self.elements['babel']:
+ prefix = '\\addto\\extras%s{' % self.babel.get_language()
+ suffix = '}'
+ else: # babel is disabled (mainly for Japanese environment)
+ prefix = ''
+ suffix = ''
+
+ return ('%s\\def%s{%s}%s\n' % (prefix, name, definition, suffix))
+
+ def _make_visit_admonition(name): # type: ignore
+ # type: (str) -> Callable[[LaTeXTranslator, nodes.Element], None]
+ warnings.warn('LaTeXTranslator._make_visit_admonition() is deprecated.',
+ RemovedInSphinx30Warning)
+
+ def visit_admonition(self, node):
+ # type: (nodes.Element) -> None
+ self.body.append('\n\\begin{sphinxadmonition}{%s}{%s:}' %
+ (name, admonitionlabels[name]))
+ return visit_admonition
+
+ def generate_numfig_format(self, builder):
+ # type: (LaTeXBuilder) -> str
+ warnings.warn('generate_numfig_format() is deprecated.',
+ RemovedInSphinx40Warning)
+ ret = [] # type: List[str]
+ figure = self.builder.config.numfig_format['figure'].split('%s', 1)
+ if len(figure) == 1:
+ ret.append('\\def\\fnum@figure{%s}\n' %
+ str(figure[0]).strip().translate(tex_escape_map))
+ else:
+ definition = escape_abbr(str(figure[0]).translate(tex_escape_map))
+ ret.append(self.babel_renewcommand('\\figurename', definition))
+ ret.append('\\makeatletter\n')
+ ret.append('\\def\\fnum@figure{\\figurename\\thefigure{}%s}\n' %
+ str(figure[1]).translate(tex_escape_map))
+ ret.append('\\makeatother\n')
+
+ table = self.builder.config.numfig_format['table'].split('%s', 1)
+ if len(table) == 1:
+ ret.append('\\def\\fnum@table{%s}\n' %
+ str(table[0]).strip().translate(tex_escape_map))
+ else:
+ definition = escape_abbr(str(table[0]).translate(tex_escape_map))
+ ret.append(self.babel_renewcommand('\\tablename', definition))
+ ret.append('\\makeatletter\n')
+ ret.append('\\def\\fnum@table{\\tablename\\thetable{}%s}\n' %
+ str(table[1]).translate(tex_escape_map))
+ ret.append('\\makeatother\n')
+
+ codeblock = self.builder.config.numfig_format['code-block'].split('%s', 1)
+ if len(codeblock) == 1:
+ pass # FIXME
+ else:
+ definition = str(codeblock[0]).strip().translate(tex_escape_map)
+ ret.append(self.babel_renewcommand('\\literalblockname', definition))
+ if codeblock[1]:
+ pass # FIXME
+
+ return ''.join(ret)
+
# Import old modules here for compatibility
-# They should be imported after `LaTeXTranslator` to avoid recursive import.
-#
-# refs: https://github.com/sphinx-doc/sphinx/issues/4889
from sphinx.builders.latex.transforms import URI_SCHEMES, ShowUrlsTransform # NOQA
+from sphinx.builders.latex.util import ExtBabel # NOQA
+
+
+deprecated_alias('sphinx.writers.latex',
+ {
+ 'ShowUrlsTransform': ShowUrlsTransform,
+ 'URI_SCHEMES': URI_SCHEMES,
+ },
+ RemovedInSphinx30Warning)
+deprecated_alias('sphinx.writers.latex',
+ {
+ 'ExtBabel': ExtBabel,
+ },
+ RemovedInSphinx40Warning)
# FIXME: Workaround to avoid circular import
# refs: https://github.com/sphinx-doc/sphinx/issues/5433