diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index 91585e402a2..93f97d028ef 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -164,15 +164,15 @@ def get_colspec(self) -> str: return self.colspec elif self.colwidths and 'colwidths-given' in self.classes: total = sum(self.colwidths) - colspecs = ['\\X{%d}{%d}' % (width, total) for width in self.colwidths] + colspecs = [r'\X{%d}{%d}' % (width, total) for width in self.colwidths] return '{|%s|}' % '|'.join(colspecs) + CR elif self.has_problematic: - return '{|*{%d}{\\X{1}{%d}|}}' % (self.colcount, self.colcount) + CR + return r'{|*{%d}{\X{1}{%d}|}}' % (self.colcount, self.colcount) + CR elif self.get_table_type() == 'tabulary': # sphinx.sty sets T to be J by default. return '{|' + ('T|' * self.colcount) + '}' + CR elif self.has_oldproblematic: - return '{|*{%d}{\\X{1}{%d}|}}' % (self.colcount, self.colcount) + CR + return r'{|*{%d}{\X{1}{%d}|}}' % (self.colcount, self.colcount) + CR else: return '{|' + ('l|' * self.colcount) + '}' + CR @@ -253,19 +253,19 @@ def rstdim_to_latexdim(width_str: str, scale: int = 100) -> str: if scale == 100: float(amount) # validate amount is float if unit in ('', "px"): - res = "%s\\sphinxpxdimen" % amount + res = r"%s\sphinxpxdimen" % amount elif unit == 'pt': res = '%sbp' % amount # convert to 'bp' elif unit == "%": - res = "%.3f\\linewidth" % (float(amount) / 100.0) + res = r"%.3f\linewidth" % (float(amount) / 100.0) else: amount_float = float(amount) * scale / 100.0 if unit in ('', "px"): - res = "%.5f\\sphinxpxdimen" % amount_float + res = r"%.5f\sphinxpxdimen" % amount_float elif unit == 'pt': res = '%.5fbp' % amount_float elif unit == "%": - res = "%.5f\\linewidth" % (amount_float / 100.0) + res = r"%.5f\linewidth" % (amount_float / 100.0) else: res = "%.5f%s" % (amount_float, unit) return res @@ -373,9 +373,9 @@ def __init__(self, document: nodes.document, builder: "LaTeXBuilder", 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}' + CR + - '\\ChNameVar{\\Large\\normalfont\\sffamily}' + CR + - '\\ChTitleVar{\\Large\\normalfont\\sffamily}') + self.elements['fncychap'] = (r'\usepackage[Sonny]{fncychap}' + CR + + r'\ChNameVar{\Large\normalfont\sffamily}' + CR + + r'\ChTitleVar{\Large\normalfont\sffamily}') self.babel = self.builder.babel if self.config.language and not self.babel.is_supported_language(): @@ -400,19 +400,19 @@ def __init__(self, document: nodes.document, builder: "LaTeXBuilder", logger.warning(__('too large :maxdepth:, ignored.')) tocdepth = len(LATEXSECTIONNAMES) - 2 - self.elements['tocdepth'] = '\\setcounter{tocdepth}{%d}' % tocdepth + self.elements['tocdepth'] = r'\setcounter{tocdepth}{%d}' % tocdepth minsecnumdepth = max(minsecnumdepth, tocdepth) 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}' %\ + self.elements['secnumdepth'] = r'\setcounter{secnumdepth}{%d}' %\ minsecnumdepth contentsname = document.get('contentsname') if contentsname: - self.elements['contentsname'] = self.babel_renewcommand('\\contentsname', + self.elements['contentsname'] = self.babel_renewcommand(r'\contentsname', contentsname) if self.elements['maxlistdepth']: @@ -420,8 +420,7 @@ def __init__(self, document: nodes.document, builder: "LaTeXBuilder", if sphinxpkgoptions: self.elements['sphinxpkgoptions'] = '[,%s]' % ','.join(sphinxpkgoptions) if self.elements['sphinxsetup']: - self.elements['sphinxsetup'] = ('\\sphinxsetup{%s}' % - self.elements['sphinxsetup']) + self.elements['sphinxsetup'] = (r'\sphinxsetup{%s}' % self.elements['sphinxsetup']) if self.elements['extraclassoptions']: self.elements['classoptions'] += ',' + \ self.elements['extraclassoptions'] @@ -466,8 +465,7 @@ def astext(self) -> str: def hypertarget(self, id: str, withdoc: bool = True, anchor: bool = True) -> str: if withdoc: id = self.curfilestack[-1] + ':' + id - return ('\\phantomsection' if anchor else '') + \ - '\\label{%s}' % self.idescape(id) + return (r'\phantomsection' if anchor else '') + r'\label{%s}' % self.idescape(id) def hypertarget_to(self, node: Element, anchor: bool = False) -> str: labels = ''.join(self.hypertarget(node_id, anchor=False) for node_id in node['ids']) @@ -477,48 +475,48 @@ def hypertarget_to(self, node: Element, anchor: bool = False) -> str: return labels def hyperlink(self, id: str) -> str: - return '{\\hyperref[%s]{' % self.idescape(id) + return r'{\hyperref[%s]{' % self.idescape(id) def hyperpageref(self, id: str) -> str: - return '\\autopageref*{%s}' % self.idescape(id) + return r'\autopageref*{%s}' % self.idescape(id) def escape(self, s: str) -> str: return texescape.escape(s, self.config.latex_engine) def idescape(self, id: str) -> str: - return '\\detokenize{%s}' % str(id).translate(tex_replace_map).\ + return r'\detokenize{%s}' % str(id).translate(tex_replace_map).\ encode('ascii', 'backslashreplace').decode('ascii').\ replace('\\', '_') def babel_renewcommand(self, command: str, definition: str) -> str: if self.elements['multilingual']: - prefix = '\\addto\\captions%s{' % self.babel.get_language() + prefix = r'\addto\captions%s{' % self.babel.get_language() suffix = '}' else: # babel is disabled (mainly for Japanese environment) prefix = '' suffix = '' - return '%s\\renewcommand{%s}{%s}%s' % (prefix, command, definition, suffix) + CR + return r'%s\renewcommand{%s}{%s}%s' % (prefix, command, definition, suffix) + CR def generate_indices(self) -> str: def generate(content: List[Tuple[str, List[IndexEntry]]], collapsed: bool) -> None: - ret.append('\\begin{sphinxtheindex}' + CR) - ret.append('\\let\\bigletter\\sphinxstyleindexlettergroup' + CR) + ret.append(r'\begin{sphinxtheindex}' + CR) + ret.append(r'\let\bigletter\sphinxstyleindexlettergroup' + CR) for i, (letter, entries) in enumerate(content): if i > 0: - ret.append('\\indexspace' + CR) - ret.append('\\bigletter{%s}' % self.escape(letter) + CR) + ret.append(r'\indexspace' + CR) + ret.append(r'\bigletter{%s}' % self.escape(letter) + CR) for entry in entries: if not entry[3]: continue - ret.append('\\item\\relax\\sphinxstyleindexentry{%s}' % + ret.append(r'\item\relax\sphinxstyleindexentry{%s}' % self.encode(entry[0])) if entry[4]: # add "extra" info - ret.append('\\sphinxstyleindexextra{%s}' % self.encode(entry[4])) - ret.append('\\sphinxstyleindexpageref{%s:%s}' % + ret.append(r'\sphinxstyleindexextra{%s}' % self.encode(entry[4])) + ret.append(r'\sphinxstyleindexpageref{%s:%s}' % (entry[2], self.idescape(entry[3])) + CR) - ret.append('\\end{sphinxtheindex}' + CR) + ret.append(r'\end{sphinxtheindex}' + CR) ret = [] # latex_domain_indices can be False/True or a list of index names @@ -534,7 +532,7 @@ def generate(content: List[Tuple[str, List[IndexEntry]]], collapsed: bool) -> No self.builder.docnames) if not content: continue - ret.append('\\renewcommand{\\indexname}{%s}' % indexcls.localname + CR) + ret.append(r'\renewcommand{\indexname}{%s}' % indexcls.localname + CR) generate(content, collapsed) return ''.join(ret) @@ -564,7 +562,7 @@ def visit_document(self, node: Element) -> None: self.first_document = 0 elif self.first_document == 0: # ... and all others are the appendices - self.body.append(CR + '\\appendix' + CR) + self.body.append(CR + r'\appendix' + CR) self.first_document = -1 if 'docname' in node: self.body.append(self.hypertarget(':doc')) @@ -597,11 +595,11 @@ def depart_problematic(self, node: Element) -> None: def visit_topic(self, node: Element) -> None: self.in_minipage = 1 - self.body.append(CR + '\\begin{sphinxShadowBox}' + CR) + self.body.append(CR + r'\begin{sphinxShadowBox}' + CR) def depart_topic(self, node: Element) -> None: self.in_minipage = 0 - self.body.append('\\end{sphinxShadowBox}' + CR) + self.body.append(r'\end{sphinxShadowBox}' + CR) visit_sidebar = visit_topic depart_sidebar = depart_topic @@ -613,20 +611,20 @@ def depart_glossary(self, node: Element) -> None: def visit_productionlist(self, node: Element) -> None: self.body.append(BLANKLINE) - self.body.append('\\begin{productionlist}' + CR) + self.body.append(r'\begin{productionlist}' + CR) self.in_production_list = 1 def depart_productionlist(self, node: Element) -> None: - self.body.append('\\end{productionlist}' + BLANKLINE) + self.body.append(r'\end{productionlist}' + BLANKLINE) self.in_production_list = 0 def visit_production(self, node: Element) -> None: if node['tokenname']: tn = node['tokenname'] self.body.append(self.hypertarget('grammar-token-' + tn)) - self.body.append('\\production{%s}{' % self.encode(tn)) + self.body.append(r'\production{%s}{' % self.encode(tn)) else: - self.body.append('\\productioncont{') + self.body.append(r'\productioncont{') def depart_production(self, node: Element) -> None: self.body.append('}' + CR) @@ -681,7 +679,7 @@ def visit_title(self, node: Element) -> None: logger.warning(__('encountered title node not in section, topic, table, ' 'admonition or sidebar'), location=node) - self.body.append('\\sphinxstyleothertitle{') + self.body.append(r'\sphinxstyleothertitle{') self.context.append('}' + CR) self.in_title = 1 @@ -694,7 +692,7 @@ def depart_title(self, node: Element) -> None: def visit_subtitle(self, node: Element) -> None: if isinstance(node.parent, nodes.sidebar): - self.body.append('\\sphinxstylesidebarsubtitle{') + self.body.append(r'\sphinxstylesidebarsubtitle{') self.context.append('}' + CR) else: self.context.append('') @@ -705,18 +703,18 @@ def depart_subtitle(self, node: Element) -> None: def visit_desc(self, node: Element) -> None: if self.config.latex_show_urls == 'footnote': self.body.append(BLANKLINE) - self.body.append('\\begin{savenotes}\\begin{fulllineitems}' + CR) + self.body.append(r'\begin{savenotes}\begin{fulllineitems}' + CR) else: self.body.append(BLANKLINE) - self.body.append('\\begin{fulllineitems}' + CR) + self.body.append(r'\begin{fulllineitems}' + CR) if self.table: self.table.has_problematic = True def depart_desc(self, node: Element) -> None: if self.config.latex_show_urls == 'footnote': - self.body.append(CR + '\\end{fulllineitems}\\end{savenotes}' + BLANKLINE) + self.body.append(CR + r'\end{fulllineitems}\end{savenotes}' + BLANKLINE) else: - self.body.append(CR + '\\end{fulllineitems}' + BLANKLINE) + self.body.append(CR + r'\end{fulllineitems}' + BLANKLINE) def _visit_signature_line(self, node: Element) -> None: for child in node: @@ -739,14 +737,14 @@ def visit_desc_signature(self, node: Element) -> None: self._visit_signature_line(node) else: self.body.append('%' + CR) - self.body.append('\\pysigstartmultiline' + CR) + self.body.append(r'\pysigstartmultiline' + CR) def depart_desc_signature(self, node: Element) -> None: if not node.get('is_multiline'): self._depart_signature_line(node) else: self.body.append('%' + CR) - self.body.append('\\pysigstopmultiline') + self.body.append(r'\pysigstopmultiline') def visit_desc_signature_line(self, node: Element) -> None: self._visit_signature_line(node) @@ -825,8 +823,8 @@ def depart_desc_content(self, node: Element) -> None: def visit_seealso(self, node: Element) -> None: self.body.append(BLANKLINE) - self.body.append('\\sphinxstrong{%s:}' % admonitionlabels['seealso'] + CR) - self.body.append('\\nopagebreak' + BLANKLINE) + self.body.append(r'\sphinxstrong{%s:}' % admonitionlabels['seealso'] + CR) + self.body.append(r'\nopagebreak' + BLANKLINE) def depart_seealso(self, node: Element) -> None: self.body.append(BLANKLINE) @@ -834,7 +832,7 @@ def depart_seealso(self, node: Element) -> None: def visit_rubric(self, node: Element) -> None: if len(node) == 1 and node.astext() in ('Footnotes', _('Footnotes')): raise nodes.SkipNode - self.body.append('\\subsubsection*{') + self.body.append(r'\subsubsection*{') self.context.append('}' + CR) self.in_title = 1 @@ -846,23 +844,23 @@ def visit_footnote(self, node: Element) -> None: self.in_footnote += 1 label = cast(nodes.label, node[0]) if 'auto' not in node: - self.body.append('\\sphinxstepexplicit ') + self.body.append(r'\sphinxstepexplicit ') if self.in_parsed_literal: - self.body.append('\\begin{footnote}[%s]' % label.astext()) + self.body.append(r'\begin{footnote}[%s]' % label.astext()) else: self.body.append('%' + CR) - self.body.append('\\begin{footnote}[%s]' % label.astext()) + self.body.append(r'\begin{footnote}[%s]' % label.astext()) if 'auto' not in node: - self.body.append('\\phantomsection' - '\\label{\\thesphinxscope.%s}%%' % label.astext() + CR) - self.body.append('\\sphinxAtStartFootnote' + CR) + self.body.append(r'\phantomsection' + r'\label{\thesphinxscope.%s}%%' % label.astext() + CR) + self.body.append(r'\sphinxAtStartFootnote' + CR) def depart_footnote(self, node: Element) -> None: if self.in_parsed_literal: - self.body.append('\\end{footnote}') + self.body.append(r'\end{footnote}') else: self.body.append('%' + CR) - self.body.append('\\end{footnote}') + self.body.append(r'\end{footnote}') self.in_footnote -= 1 def visit_label(self, node: Element) -> None: @@ -950,25 +948,24 @@ def visit_row(self, node: Element) -> None: self.body.append('&') if cell.width == 1: # insert suitable strut for equalizing row heights in given multirow - self.body.append('\\sphinxtablestrut{%d}' % cell.cell_id) + self.body.append(r'\sphinxtablestrut{%d}' % cell.cell_id) else: # use \multicolumn for wide multirow cell - self.body.append('\\multicolumn{%d}{|l|}' - '{\\sphinxtablestrut{%d}}' % + self.body.append(r'\multicolumn{%d}{|l|}\sphinxtablestrut{%d}}' % (cell.width, cell.cell_id)) def depart_row(self, node: Element) -> None: - self.body.append('\\\\' + CR) + self.body.append(r'\\' + CR) 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] if all(underlined): - self.body.append('\\hline') + self.body.append(r'\hline') else: i = 0 underlined.extend([False]) # sentinel while i < len(underlined): if underlined[i] is True: j = underlined[i:].index(False) - self.body.append('\\cline{%d-%d}' % (i + 1, i + j)) + self.body.append(r'\cline{%d-%d}' % (i + 1, i + j)) i += j i += 1 self.table.row += 1 @@ -982,22 +979,22 @@ def visit_entry(self, node: Element) -> None: if cell.width > 1: if self.config.latex_use_latex_multicolumn: if self.table.col == 0: - self.body.append('\\multicolumn{%d}{|l|}{%%' % cell.width + CR) + self.body.append(r'\multicolumn{%d}{|l|}{%%' % cell.width + CR) else: - self.body.append('\\multicolumn{%d}{l|}{%%' % cell.width + CR) + self.body.append(r'\multicolumn{%d}{l|}{%%' % cell.width + CR) context = '}%' + CR else: - self.body.append('\\sphinxstartmulticolumn{%d}%%' % cell.width + CR) - context = '\\sphinxstopmulticolumn' + CR + self.body.append(r'\sphinxstartmulticolumn{%d}%%' % cell.width + CR) + context = r'\sphinxstopmulticolumn' + CR if cell.height > 1: # \sphinxmultirow 2nd arg "cell_id" will serve as id for LaTeX macros as well - self.body.append('\\sphinxmultirow{%d}{%d}{%%' % (cell.height, cell.cell_id) + CR) + self.body.append(r'\sphinxmultirow{%d}{%d}{%%' % (cell.height, cell.cell_id) + CR) context = '}%' + CR + context if cell.width > 1 or cell.height > 1: - self.body.append('\\begin{varwidth}[t]{\\sphinxcolwidth{%d}{%d}}' + self.body.append(r'\begin{varwidth}[t]{\sphinxcolwidth{%d}{%d}}' % (cell.width, self.table.colcount) + CR) - context = ('\\par' + CR + '\\vskip-\\baselineskip' - '\\vbox{\\hbox{\\strut}}\\end{varwidth}%' + CR + context) + context = (r'\par' + CR + r'\vskip-\baselineskip' + r'\vbox{\hbox{\strut}}\end{varwidth}%' + CR + context) self.needs_linetrimming = 1 if len(node.traverse(nodes.paragraph)) >= 2: self.table.has_oldproblematic = True @@ -1005,7 +1002,7 @@ def visit_entry(self, node: Element) -> None: if len(node) == 1 and isinstance(node[0], nodes.paragraph) and node.astext() == '': pass else: - self.body.append('\\sphinxstyletheadfamily ') + self.body.append(r'\sphinxstyletheadfamily ') if self.needs_linetrimming: self.pushbody([]) self.context.append(context) @@ -1036,11 +1033,10 @@ def depart_entry(self, node: Element) -> None: if nextcell.width == 1: # insert suitable strut for equalizing row heights in multirow # they also serve to clear colour panels which would hide the text - self.body.append('\\sphinxtablestrut{%d}' % nextcell.cell_id) + self.body.append(r'\sphinxtablestrut{%d}' % nextcell.cell_id) else: # use \multicolumn for wide multirow cell - self.body.append('\\multicolumn{%d}{l|}' - '{\\sphinxtablestrut{%d}}' % + self.body.append(r'\multicolumn{%d}{l|}{\sphinxtablestrut{%d}}' % (nextcell.width, nextcell.cell_id)) def visit_acks(self, node: Element) -> None: @@ -1055,13 +1051,13 @@ def visit_acks(self, node: Element) -> None: def visit_bullet_list(self, node: Element) -> None: if not self.compact_list: - self.body.append('\\begin{itemize}' + CR) + self.body.append(r'\begin{itemize}' + CR) if self.table: self.table.has_problematic = True def depart_bullet_list(self, node: Element) -> None: if not self.compact_list: - self.body.append('\\end{itemize}' + CR) + self.body.append(r'\end{itemize}' + CR) def visit_enumerated_list(self, node: Element) -> None: def get_enumtype(node: Element) -> str: @@ -1086,16 +1082,16 @@ def get_nested_level(node: Element) -> int: prefix = node.get('prefix', '') suffix = node.get('suffix', '.') - self.body.append('\\begin{enumerate}' + CR) - self.body.append('\\sphinxsetlistlabels{%s}{%s}{%s}{%s}{%s}%%' % + self.body.append(r'\begin{enumerate}' + CR) + self.body.append(r'\sphinxsetlistlabels{%s}{%s}{%s}{%s}{%s}%%' % (style, enum, enumnext, prefix, suffix) + CR) if 'start' in node: - self.body.append('\\setcounter{%s}{%d}' % (enum, node['start'] - 1) + CR) + self.body.append(r'\setcounter{%s}{%d}' % (enum, node['start'] - 1) + CR) if self.table: self.table.has_problematic = True def depart_enumerated_list(self, node: Element) -> None: - self.body.append('\\end{enumerate}' + CR) + self.body.append(r'\end{enumerate}' + CR) def visit_list_item(self, node: Element) -> None: # Append "{}" in case the next character is "[", which would break @@ -1106,12 +1102,12 @@ def depart_list_item(self, node: Element) -> None: self.body.append(CR) def visit_definition_list(self, node: Element) -> None: - self.body.append('\\begin{description}' + CR) + self.body.append(r'\begin{description}' + CR) if self.table: self.table.has_problematic = True def depart_definition_list(self, node: Element) -> None: - self.body.append('\\end{description}' + CR) + self.body.append(r'\end{description}' + CR) def visit_definition_list_item(self, node: Element) -> None: pass @@ -1123,11 +1119,11 @@ def visit_term(self, node: Element) -> None: self.in_term += 1 ctx = '' if node.get('ids'): - ctx = '\\phantomsection' + ctx = r'\phantomsection' for node_id in node['ids']: ctx += self.hypertarget(node_id, anchor=False) - ctx += '}] \\leavevmode' - self.body.append('\\item[{') + ctx += r'}] \leavevmode' + self.body.append(r'\item[{') self.context.append(ctx) def depart_term(self, node: Element) -> None: @@ -1147,12 +1143,12 @@ def depart_definition(self, node: Element) -> None: self.body.append(CR) def visit_field_list(self, node: Element) -> None: - self.body.append('\\begin{quote}\\begin{description}' + CR) + self.body.append(r'\begin{quote}\begin{description}' + CR) if self.table: self.table.has_problematic = True def depart_field_list(self, node: Element) -> None: - self.body.append('\\end{description}\\end{quote}' + CR) + self.body.append(r'\end{description}\end{quote}' + CR) def visit_field(self, node: Element) -> None: pass @@ -1172,7 +1168,7 @@ def visit_paragraph(self, node: Element) -> None: not isinstance(node.parent[index - 1], nodes.paragraph) and not isinstance(node.parent[index - 1], nodes.compound)): # insert blank line, if the paragraph follows a non-paragraph node in a compound - self.body.append('\\noindent' + CR) + self.body.append(r'\noindent' + CR) elif index == 1 and isinstance(node.parent, (nodes.footnote, footnotetext)): # don't insert blank line, if the paragraph is second child of a footnote # (first one is label node) @@ -1181,33 +1177,33 @@ def visit_paragraph(self, node: Element) -> None: # the \sphinxAtStartPar is to allow hyphenation of first word of # a paragraph in narrow contexts such as in a table cell # added as two items (cf. line trimming in depart_entry()) - self.body.extend([CR, '\\sphinxAtStartPar' + CR]) + self.body.extend([CR, r'\sphinxAtStartPar' + CR]) def depart_paragraph(self, node: Element) -> None: self.body.append(CR) def visit_centered(self, node: Element) -> None: - self.body.append(CR + '\\begin{center}') + self.body.append(CR + r'\begin{center}') if self.table: self.table.has_problematic = True def depart_centered(self, node: Element) -> None: - self.body.append(CR + '\\end{center}') + self.body.append(CR + r'\end{center}') def visit_hlist(self, node: Element) -> None: self.compact_list += 1 ncolumns = node['ncolumns'] if self.compact_list > 1: - self.body.append('\\setlength{\\multicolsep}{0pt}' + CR) - self.body.append('\\begin{multicols}{' + ncolumns + '}\\raggedright' + CR) - self.body.append('\\begin{itemize}\\setlength{\\itemsep}{0pt}' - '\\setlength{\\parskip}{0pt}' + CR) + self.body.append(r'\setlength{\multicolsep}{0pt}' + CR) + self.body.append(r'\begin{multicols}{' + ncolumns + '}\raggedright' + CR) + self.body.append(r'\begin{itemize}\setlength{\itemsep}{0pt}' + r'\setlength{\parskip}{0pt}' + CR) if self.table: self.table.has_problematic = True def depart_hlist(self, node: Element) -> None: self.compact_list -= 1 - self.body.append('\\end{itemize}\\raggedcolumns\\end{multicols}' + CR) + self.body.append(r'\end{itemize}\raggedcolumns\end{multicols}' + CR) def visit_hlistcol(self, node: Element) -> None: pass @@ -1217,7 +1213,7 @@ def depart_hlistcol(self, node: Element) -> None: # some testing with long items showed that columns may be too uneven. # And in case only of short items, the automatic column breaks should # match the ones pre-computed by the hlist() directive. - # self.body.append('\\columnbreak\n') + # self.body.append(r'\columnbreak\n') pass def latex_image_length(self, width_str: str, scale: int = 100) -> str: @@ -1265,14 +1261,14 @@ def visit_image(self, node: Element) -> None: align_prepost = { # By default latex aligns the top of an image. (1, 'top'): ('', ''), - (1, 'middle'): ('\\raisebox{-0.5\\height}{', '}'), - (1, 'bottom'): ('\\raisebox{-\\height}{', '}'), - (0, 'center'): ('{\\hspace*{\\fill}', '\\hspace*{\\fill}}'), + (1, 'middle'): (r'\raisebox{-0.5\height}{', '}'), + (1, 'bottom'): (r'\raisebox{-\height}{', '}'), + (0, 'center'): (r'{\hspace*{\fill}', r'\hspace*{\fill}}'), # These 2 don't exactly do the right thing. The image should # be floated alongside the paragraph. See # https://www.w3.org/TR/html4/struct/objects.html#adef-align-IMG - (0, 'left'): ('{', '\\hspace*{\\fill}}'), - (0, 'right'): ('{\\hspace*{\\fill}', '}'), + (0, 'left'): ('{', r'\hspace*{\fill}}'), + (0, 'right'): (r'{\hspace*{\fill}', '}'), } try: pre.append(align_prepost[is_inline, node['align']][0]) @@ -1280,10 +1276,10 @@ def visit_image(self, node: Element) -> None: except KeyError: pass if self.in_parsed_literal: - pre.append('{\\sphinxunactivateextrasandspace ') + pre.append(r'{\sphinxunactivateextrasandspace ') post.append('}') if not is_inline and not has_hyperlink: - pre.append(CR + '\\noindent') + pre.append(CR + r'\noindent') post.append(CR) pre.reverse() if node['uri'] in self.builder.images: @@ -1304,10 +1300,10 @@ def visit_image(self, node: Element) -> None: if self.in_title and base: # Lowercase tokens forcely because some fncychap themes capitalize # the options of \sphinxincludegraphics unexpectly (ex. WIDTH=...). - self.body.append('\\lowercase{\\sphinxincludegraphics%s}{{%s}%s}' % + self.body.append(r'\lowercase{\sphinxincludegraphics%s}{{%s}%s}' % (options, base, ext)) else: - self.body.append('\\sphinxincludegraphics%s{{%s}%s}' % + self.body.append(r'\sphinxincludegraphics%s{{%s}%s}' % (options, base, ext)) self.body.extend(post) @@ -1323,14 +1319,14 @@ def visit_figure(self, node: Element) -> None: if 'width' in node: length = self.latex_image_length(node['width']) if length: - self.body.append('\\begin{sphinxfigure-in-table}[%s]' % length + CR) - self.body.append('\\centering' + CR) + self.body.append(r'\begin{sphinxfigure-in-table}[%s]' % length + CR) + self.body.append(r'\centering' + CR) else: - self.body.append('\\begin{sphinxfigure-in-table}' + CR) - self.body.append('\\centering' + CR) + self.body.append(r'\begin{sphinxfigure-in-table}' + CR) + self.body.append(r'\centering' + CR) if any(isinstance(child, nodes.caption) for child in node): - self.body.append('\\capstart') - self.context.append('\\end{sphinxfigure-in-table}\\relax' + CR) + self.body.append(r'\capstart') + self.context.append(r'\end{sphinxfigure-in-table}\relax' + CR) elif node.get('align', '') in ('left', 'right'): length = None if 'width' in node: @@ -1339,19 +1335,19 @@ def visit_figure(self, node: Element) -> None: length = self.latex_image_length(node[0]['width']) self.body.append(BLANKLINE) # Insert a blank line to prevent infinite loop # https://github.com/sphinx-doc/sphinx/issues/7059 - self.body.append('\\begin{wrapfigure}{%s}{%s}' % + self.body.append(r'\begin{wrapfigure}{%s}{%s}' % ('r' if node['align'] == 'right' else 'l', length or '0pt') + CR) - self.body.append('\\centering') - self.context.append('\\end{wrapfigure}' + CR) + self.body.append(r'\centering') + self.context.append(r'\end{wrapfigure}' + CR) elif self.in_minipage: - self.body.append(CR + '\\begin{center}') - self.context.append('\\end{center}' + CR) + self.body.append(CR + r'\begin{center}') + self.context.append(r'\end{center}' + CR) else: - self.body.append(CR + '\\begin{figure}[%s]' % align + CR) - self.body.append('\\centering' + CR) + self.body.append(CR + r'\begin{figure}[%s]' % align + CR) + self.body.append(r'\centering' + CR) if any(isinstance(child, nodes.caption) for child in node): - self.body.append('\\capstart' + CR) - self.context.append('\\end{figure}' + CR) + self.body.append(r'\capstart' + CR) + self.context.append(r'\end{figure}' + CR) def depart_figure(self, node: Element) -> None: self.body.append(self.context.pop()) @@ -1359,13 +1355,13 @@ def depart_figure(self, node: Element) -> None: def visit_caption(self, node: Element) -> None: self.in_caption += 1 if isinstance(node.parent, captioned_literal_block): - self.body.append('\\sphinxSetupCaptionForVerbatim{') + self.body.append(r'\sphinxSetupCaptionForVerbatim{') elif self.in_minipage and isinstance(node.parent, nodes.figure): - self.body.append('\\captionof{figure}{') + self.body.append(r'\captionof{figure}{') elif self.table and node.parent.tagname == 'figure': - self.body.append('\\sphinxfigcaption{') + self.body.append(r'\sphinxfigcaption{') else: - self.body.append('\\caption{') + self.body.append(r'\caption{') def depart_caption(self, node: Element) -> None: self.body.append('}') @@ -1375,27 +1371,27 @@ def depart_caption(self, node: Element) -> None: self.in_caption -= 1 def visit_legend(self, node: Element) -> None: - self.body.append(CR + '\\begin{sphinxlegend}') + self.body.append(CR + r'\begin{sphinxlegend}') def depart_legend(self, node: Element) -> None: - self.body.append('\\end{sphinxlegend}' + CR) + self.body.append(r'\end{sphinxlegend}' + CR) def visit_admonition(self, node: Element) -> None: - self.body.append(CR + '\\begin{sphinxadmonition}{note}') + self.body.append(CR + r'\begin{sphinxadmonition}{note}') self.no_latex_floats += 1 def depart_admonition(self, node: Element) -> None: - self.body.append('\\end{sphinxadmonition}' + CR) + self.body.append(r'\end{sphinxadmonition}' + CR) self.no_latex_floats -= 1 def _visit_named_admonition(self, node: Element) -> None: label = admonitionlabels[node.tagname] - self.body.append(CR + '\\begin{sphinxadmonition}{%s}{%s:}' % + self.body.append(CR + r'\begin{sphinxadmonition}{%s}{%s:}' % (node.tagname, label)) self.no_latex_floats += 1 def _depart_named_admonition(self, node: Element) -> None: - self.body.append('\\end{sphinxadmonition}' + CR) + self.body.append(r'\end{sphinxadmonition}' + CR) self.no_latex_floats -= 1 visit_attention = _visit_named_admonition @@ -1473,11 +1469,11 @@ def depart_target(self, node: Element) -> None: pass def visit_attribution(self, node: Element) -> None: - self.body.append(CR + '\\begin{flushright}' + CR) + self.body.append(CR + r'\begin{flushright}' + CR) self.body.append('---') def depart_attribution(self, node: Element) -> None: - self.body.append(CR + '\\end{flushright}' + CR) + self.body.append(CR + r'\end{flushright}' + CR) def visit_index(self, node: Element) -> None: def escape(value: str) -> str: @@ -1495,7 +1491,7 @@ def style(string: str) -> str: if match: return match.expand(r'\\spxentry{\1}\\spxextra{\2}') else: - return '\\spxentry{%s}' % string + return r'\spxentry{%s}' % string if not node.get('inline', True): self.body.append(CR) @@ -1542,7 +1538,7 @@ def style(string: str) -> str: except ValueError as err: logger.warning(str(err)) if not node.get('inline', True): - self.body.append('\\ignorespaces ') + self.body.append(r'\ignorespaces ') raise nodes.SkipNode def visit_raw(self, node: Element) -> None: @@ -1602,12 +1598,12 @@ def visit_reference(self, node: Element) -> None: else: if len(node) == 1 and uri == node[0]: if node.get('nolinkurl'): - self.body.append('\\sphinxnolinkurl{%s}' % self.encode_uri(uri)) + self.body.append(r'\sphinxnolinkurl{%s}' % self.encode_uri(uri)) else: - self.body.append('\\sphinxurl{%s}' % self.encode_uri(uri)) + self.body.append(r'\sphinxurl{%s}' % self.encode_uri(uri)) raise nodes.SkipNode else: - self.body.append('\\sphinxhref{%s}{' % self.encode_uri(uri)) + self.body.append(r'\sphinxhref{%s}{' % self.encode_uri(uri)) self.context.append('}') def depart_reference(self, node: Element) -> None: @@ -1621,16 +1617,16 @@ def visit_number_reference(self, node: Element) -> None: else: id = node.get('refuri', '')[1:].replace('#', ':') - title = self.escape(node.get('title', '%s')).replace('\\%s', '%s') - if '\\{name\\}' in title or '\\{number\\}' in title: + title = self.escape(node.get('title', '%s')).replace(r'\%s', '%s') + if r'\{name\}' in title or r'\{number\}' in title: # new style format (cf. "Fig.%{number}") - title = title.replace('\\{name\\}', '{name}').replace('\\{number\\}', '{number}') - text = escape_abbr(title).format(name='\\nameref{%s}' % self.idescape(id), - number='\\ref{%s}' % self.idescape(id)) + title = title.replace(r'\{name\}', '{name}').replace(r'\{number\}', '{number}') + text = escape_abbr(title).format(name=r'\nameref{%s}' % self.idescape(id), + number=r'\ref{%s}' % self.idescape(id)) else: # old style format (cf. "Fig.%{number}") - text = escape_abbr(title) % ('\\ref{%s}' % self.idescape(id)) - hyperref = '\\hyperref[%s]{%s}' % (self.idescape(id), text) + text = escape_abbr(title) % (r'\ref{%s}' % self.idescape(id)) + hyperref = r'\hyperref[%s]{%s}' % (self.idescape(id), text) self.body.append(hyperref) raise nodes.SkipNode @@ -1704,15 +1700,15 @@ def visit_thebibliography(self, node: Element) -> None: # adjust max width of citation labels not to break the layout longest_label = longest_label[:MAX_CITATION_LABEL_LENGTH] - self.body.append(CR + '\\begin{sphinxthebibliography}{%s}' % + self.body.append(CR + r'\begin{sphinxthebibliography}{%s}' % self.encode(longest_label) + CR) def depart_thebibliography(self, node: Element) -> None: - self.body.append('\\end{sphinxthebibliography}' + CR) + self.body.append(r'\end{sphinxthebibliography}' + CR) def visit_citation(self, node: Element) -> None: label = cast(nodes.label, node[0]) - self.body.append('\\bibitem[%s]{%s:%s}' % (self.encode(label.astext()), + self.body.append(r'\bibitem[%s]{%s:%s}' % (self.encode(label.astext()), node['docname'], node['ids'][0])) def depart_citation(self, node: Element) -> None: @@ -1722,7 +1718,7 @@ def visit_citation_reference(self, node: Element) -> None: if self.in_title: pass else: - self.body.append('\\sphinxcite{%s:%s}' % (node['docname'], node['refname'])) + self.body.append(r'\sphinxcite{%s:%s}' % (node['docname'], node['refname'])) raise nodes.SkipNode def depart_citation_reference(self, node: Element) -> None: @@ -1743,7 +1739,7 @@ def visit_footnote_reference(self, node: Element) -> None: raise nodes.SkipNode def visit_footnotemark(self, node: Element) -> None: - self.body.append('\\sphinxfootnotemark[') + self.body.append(r'\sphinxfootnotemark[') def depart_footnotemark(self, node: Element) -> None: self.body.append(']') @@ -1751,15 +1747,15 @@ def depart_footnotemark(self, node: Element) -> None: def visit_footnotetext(self, node: Element) -> None: label = cast(nodes.label, node[0]) self.body.append('%' + CR) - self.body.append('\\begin{footnotetext}[%s]' - '\\phantomsection\\label{\\thesphinxscope.%s}%%' + self.body.append(r'\begin{footnotetext}[%s]' + r'\phantomsection\label{\thesphinxscope.%s}%%' % (label.astext(), label.astext()) + CR) - self.body.append('\\sphinxAtStartFootnote' + CR) + self.body.append(r'\sphinxAtStartFootnote' + CR) def depart_footnotetext(self, node: Element) -> None: # the \ignorespaces in particular for after table header use self.body.append('%' + CR) - self.body.append('\\end{footnotetext}\\ignorespaces ') + self.body.append(r'\end{footnotetext}\ignorespaces ') def visit_captioned_literal_block(self, node: Element) -> None: pass @@ -1771,13 +1767,13 @@ def visit_literal_block(self, node: Element) -> None: if node.rawsource != node.astext(): # most probably a parsed-literal block -- don't highlight self.in_parsed_literal += 1 - self.body.append('\\begin{sphinxalltt}' + CR) + self.body.append(r'\begin{sphinxalltt}' + CR) else: labels = self.hypertarget_to(node) if isinstance(node.parent, captioned_literal_block): labels += self.hypertarget_to(node.parent) if labels and not self.in_footnote: - self.body.append(CR + '\\def\\sphinxLiteralBlockLabel{' + labels + '}') + self.body.append(CR + r'\def\sphinxLiteralBlockLabel{' + labels + '}') lang = node.get('language', 'default') linenos = node.get('linenos', False) @@ -1790,57 +1786,57 @@ def visit_literal_block(self, node: Element) -> None: location=node, **highlight_args ) if self.in_footnote: - self.body.append(CR + '\\sphinxSetupCodeBlockInFootnote') - hlcode = hlcode.replace('\\begin{Verbatim}', - '\\begin{sphinxVerbatim}') + self.body.append(CR + r'\sphinxSetupCodeBlockInFootnote') + hlcode = hlcode.replace(r'\begin{Verbatim}', + r'\begin{sphinxVerbatim}') # if in table raise verbatim flag to avoid "tabulary" environment # and opt for sphinxVerbatimintable to handle caption & long lines elif self.table: self.table.has_problematic = True self.table.has_verbatim = True - hlcode = hlcode.replace('\\begin{Verbatim}', - '\\begin{sphinxVerbatimintable}') + hlcode = hlcode.replace(r'\begin{Verbatim}', + r'\begin{sphinxVerbatimintable}') else: - hlcode = hlcode.replace('\\begin{Verbatim}', - '\\begin{sphinxVerbatim}') + hlcode = hlcode.replace(r'\begin{Verbatim}', + r'\begin{sphinxVerbatim}') # get consistent trailer hlcode = hlcode.rstrip()[:-14] # strip \end{Verbatim} if self.table and not self.in_footnote: - hlcode += '\\end{sphinxVerbatimintable}' + hlcode += r'\end{sphinxVerbatimintable}' else: - hlcode += '\\end{sphinxVerbatim}' + hlcode += r'\end{sphinxVerbatim}' hllines = str(highlight_args.get('hl_lines', []))[1:-1] if hllines: - self.body.append(CR + '\\fvset{hllines={, %s,}}%%' % hllines) + self.body.append(CR + r'\fvset{hllines={, %s,}}%%' % hllines) self.body.append(CR + hlcode + CR) if hllines: - self.body.append('\\sphinxresetverbatimhllines' + CR) + self.body.append(r'\sphinxresetverbatimhllines' + CR) raise nodes.SkipNode def depart_literal_block(self, node: Element) -> None: - self.body.append(CR + '\\end{sphinxalltt}' + CR) + self.body.append(CR + r'\end{sphinxalltt}' + CR) self.in_parsed_literal -= 1 visit_doctest_block = visit_literal_block depart_doctest_block = depart_literal_block def visit_line(self, node: Element) -> None: - self.body.append('\\item[] ') + self.body.append(r'\item[] ') def depart_line(self, node: Element) -> None: self.body.append(CR) def visit_line_block(self, node: Element) -> None: if isinstance(node.parent, nodes.line_block): - self.body.append('\\item[]' + CR) - self.body.append('\\begin{DUlineblock}{\\DUlineblockindent}' + CR) + self.body.append(r'\item[]' + CR) + self.body.append(r'\begin{DUlineblock}{\DUlineblockindent}' + CR) else: - self.body.append(CR + '\\begin{DUlineblock}{0em}' + CR) + self.body.append(CR + r'\begin{DUlineblock}{0em}' + CR) if self.table: self.table.has_problematic = True def depart_line_block(self, node: Element) -> None: - self.body.append('\\end{DUlineblock}' + CR) + self.body.append(r'\end{DUlineblock}' + CR) def visit_block_quote(self, node: Element) -> None: # If the block quote contains a single object and that object @@ -1853,7 +1849,7 @@ def visit_block_quote(self, node: Element) -> None: isinstance(child, nodes.enumerated_list): done = 1 if not done: - self.body.append('\\begin{quote}' + CR) + self.body.append(r'\begin{quote}' + CR) if self.table: self.table.has_problematic = True @@ -1865,7 +1861,7 @@ def depart_block_quote(self, node: Element) -> None: isinstance(child, nodes.enumerated_list): done = 1 if not done: - self.body.append('\\end{quote}' + CR) + self.body.append(r'\end{quote}' + CR) # option node handling copied from docutils' latex writer @@ -1886,7 +1882,7 @@ def depart_option_argument(self, node: Element) -> None: pass def visit_option_group(self, node: Element) -> None: - self.body.append('\\item [') + self.body.append(r'\item [') # flag for first option self.context.append(0) @@ -1895,12 +1891,12 @@ def depart_option_group(self, node: Element) -> None: self.body.append('] ') def visit_option_list(self, node: Element) -> None: - self.body.append('\\begin{optionlist}{3cm}' + CR) + self.body.append(r'\begin{optionlist}{3cm}' + CR) if self.table: self.table.has_problematic = True def depart_option_list(self, node: Element) -> None: - self.body.append('\\end{optionlist}' + CR) + self.body.append(r'\end{optionlist}' + CR) def visit_option_list_item(self, node: Element) -> None: pass @@ -1920,13 +1916,13 @@ def depart_description(self, node: Element) -> None: pass def visit_superscript(self, node: Element) -> None: - self.body.append('$^{\\text{') + self.body.append(r'$^{\text{') def depart_superscript(self, node: Element) -> None: self.body.append('}}$') def visit_subscript(self, node: Element) -> None: - self.body.append('$_{\\text{') + self.body.append(r'$_{\text{') def depart_subscript(self, node: Element) -> None: self.body.append('}}$') @@ -1993,7 +1989,7 @@ def encode(self, text: str) -> str: if self.literal_whitespace: # Insert a blank before the newline, to avoid # ! LaTeX Error: There's no line here to end. - text = text.replace(CR, '~\\\\' + CR).replace(' ', '~') + text = text.replace(CR, r'~\\' + CR).replace(' ', '~') return text def encode_uri(self, text: str) -> str: @@ -2001,9 +1997,9 @@ def encode_uri(self, text: str) -> str: # this must be checked against hyperref package exact dealings # mainly, %, #, {, } and \ need escaping via a \ escape # in \href, the tilde is allowed and must be represented literally - return self.encode(text).replace('\\textasciitilde{}', '~').\ - replace('\\sphinxhyphen{}', '-').\ - replace('\\textquotesingle{}', "'") + return self.encode(text).replace(r'\textasciitilde{}', '~').\ + replace(r'\sphinxhyphen{}', '-').\ + replace(r'\textquotesingle{}', "'") def visit_Text(self, node: Text) -> None: text = self.encode(node.astext())