From: Matěj C. <mc...@ce...> - 2025-07-22 00:16:16
|
Hi, I have used rST for reconstruction of the ancient theological text and with the scholarly version I got plenty of footnotes (which I would like to print on the bottom of the page with per-page numbering) [1], and I found to my unpleasant surprise that rst2xetex doesn’t produce correct LaTeX footnotes, but something weird, which certainly cannot be controlled by the standard LaTeX tools. I have found this patch, and decided to at least rebase it against the current master branch. I needed to fix tests, but otherwise the test suite now seems to pass. What do you think? Best, Matěj [1] https://git.sr.ht/~mcepl/justin_susil/tree/master/item/p_just_2_apol.rst John Thorvald Wodder II (1): An implementation of --latex-footnotes Matěj Cepl (1): Fix tests to work with --latex-footnotes option. docutils/docutils/writers/latex2e/__init__.py | 93 +++-- docutils/test/data/help/rst2latex.rst | 3 +- docutils/test/test_writers/test_latex2e.py | 322 +++++++++++++++++- 3 files changed, 386 insertions(+), 32 deletions(-) -- 2.50.1 |
From: Matěj C. <mc...@ce...> - 2025-07-22 00:16:16
|
--- docutils/docutils/writers/latex2e/__init__.py | 4 +- docutils/test/data/help/rst2latex.rst | 3 +- docutils/test/test_writers/test_latex2e.py | 350 ++++++++++++++---- 3 files changed, 280 insertions(+), 77 deletions(-) diff --git a/docutils/docutils/writers/latex2e/__init__.py b/docutils/docutils/writers/latex2e/__init__.py index 1dcf13466..4b6b0b431 100644 --- a/docutils/docutils/writers/latex2e/__init__.py +++ b/docutils/docutils/writers/latex2e/__init__.py @@ -232,13 +232,13 @@ class Writer(writers.Writer): {'default': True, 'action': 'store_true', 'validator': frontend.validate_boolean}), - ), ('Footnotes with numbers by LaTeX.', ['--latex-footnotes'], {'dest': 'docutils_footnotes', 'action': 'store_false', - 'validator': frontend.validate_boolean}), + 'validator': frontend.validate_boolean}) ) + ) relative_path_settings = ('template',) settings_defaults = {} diff --git a/docutils/test/data/help/rst2latex.rst b/docutils/test/data/help/rst2latex.rst index d0ede4c63..8e7e702f4 100644 --- a/docutils/test/data/help/rst2latex.rst +++ b/docutils/test/data/help/rst2latex.rst @@ -256,5 +256,4 @@ LaTeX-Specific Options --new-column-widths Use new algorithm to determine table column widths. (future default) --docutils-footnotes Footnotes with numbers/symbols by Docutils. (default) - (The alternative, --latex-footnotes, is not - implemented yet.) +--latex-footnotes Footnotes with numbers by LaTeX. diff --git a/docutils/test/test_writers/test_latex2e.py b/docutils/test/test_writers/test_latex2e.py index 434786690..acb9dd6ed 100755 --- a/docutils/test/test_writers/test_latex2e.py +++ b/docutils/test/test_writers/test_latex2e.py @@ -47,7 +47,7 @@ def test_body(self): self.assertEqual(expected, output) -samples = {} +samples = {} # type: ignore samples['default'] = ({}, [ @@ -500,98 +500,256 @@ def test_body(self): ]) -totest_latex_footnotes['simple'] = [ -# input -["""\ -Paragraphs contain text and may contain footnote references (manually -numbered [1]_, anonymous auto-numbered [#]_, labeled auto-numbered -[#label]_, or symbolic [*]_). +class FootnoteTestCase(unittest.TestCase): -.. [1] A footnote contains body elements, consistently indented by at - least 3 spaces. + maxDiff = None + settings = { + '_disable_config': True, + 'strict_visitor': True, + 'output_encoding': 'unicode', + 'stylesheet': '', + 'language_code': 'en', + 'use_latex_toc': False, + 'use_latex_citations': False, + 'legacy_column_widths': True, + } - This is the footnote's second paragraph. + def test_footnotes(self): + for name, (settings_overrides, cases) in footnote_samples.items(): + for casenum, (rst_input, expected) in enumerate(cases): + with self.subTest(id=f'footnote_samples[{name!r}][{casenum}]'): + output = publish_parts( + source=rst_input, + writer=latex2e.Writer(), + settings_overrides=self.settings | settings_overrides + )['whole'] + self.assertEqual(expected, output) -.. [#label] Footnotes may be numbered, either manually or - automatically using a "#"-prefixed label. This footnote has a - label so it can be referred to from multiple places, both as a - footnote reference and as a hyperlink reference. -.. [#] This footnote is numbered automatically and anonymously using a - label of "#" only. +footnote_samples = { + 'simple': ({}, [ + [r""" + Paragraphs contain text and may contain footnote references (manually + numbered [1]_, anonymous auto-numbered [#]_, labeled auto-numbered + [#label]_, or symbolic [*]_). + + .. [1] A footnote contains body elements, consistently indented by at + least 3 spaces. + + This is the footnote's second paragraph. + + .. [#label] Footnotes may be numbered, either manually or + automatically using a "#"-prefixed label. This footnote has a + label so it can be referred to from multiple places, both as a + footnote reference and as a hyperlink reference. + + .. [#] This footnote is numbered automatically and anonymously using a + label of "#" only. + + .. [*] Footnotes may also use symbols, specified with a "*" label. + """, + r"""\documentclass[a4paper]{article} +% generated by Docutils <https://docutils.sourceforge.io/> +\usepackage{cmap} % fix search and cut-and-paste in Acrobat +\usepackage[T1]{fontenc} + +%%% Custom LaTeX preamble +% PDF Standard Fonts +\usepackage{mathptmx} % Times +\usepackage[scaled=.90]{helvet} +\usepackage{courier} + +%%% User specified packages and stylesheets + +%%% Fallback definitions for Docutils-specific commands + +% numerical or symbol footnotes with hyperlinks and backlinks +\providecommand*{\DUfootnotemark}[3]{% + \raisebox{1em}{\hypertarget{#1}{}}% + \hyperlink{#2}{\textsuperscript{#3}}% +} +\providecommand{\DUfootnotetext}[4]{% + \begingroup% + \renewcommand{\thefootnote}{% + \protect\raisebox{1em}{\protect\hypertarget{#1}{}}% + \protect\hyperlink{#2}{#3}}% + \footnotetext{#4}% + \endgroup% +} -.. [*] Footnotes may also use symbols, specified with a "*" label. -""", -## # expected output -head_template.substitute(dict(parts)) + r""" +% hyperlinks: +\ifdefined\hypersetup +\else + \usepackage[hyperfootnotes=false, + colorlinks=true,linkcolor=blue,urlcolor=blue]{hyperref} + \usepackage{bookmark} + \urlstyle{same} % normal text font (alternatives: tt, rm, sf) +\fi + +%%% Body +\begin{document} + +\begin{quote} Paragraphs contain text and may contain footnote references (manually -numbered\footnote{% +numbered\DUfootnotemark{footnote-reference-1}{footnote-1}{1}, anonymous auto-numbered\DUfootnotemark{footnote-reference-2}{footnote-2}{3}, labeled auto-numbered\DUfootnotemark{footnote-reference-3}{label}{2}, or symbolic\DUfootnotemark{footnote-reference-4}{footnote-3}{*}). +% +\DUfootnotetext{footnote-1}{footnote-reference-1}{1}{% A footnote contains body elements, consistently indented by at least 3 spaces. This is the footnote's second paragraph. -}, anonymous auto-numbered\footnote{% -This footnote is numbered automatically and anonymously using a -label of \textquotedbl{}\#\textquotedbl{} only. -}, labeled auto-numbered\footnote{% +} +% +\DUfootnotetext{label}{footnote-reference-3}{2}{\phantomsection\label{label}% Footnotes may be numbered, either manually or automatically using a \textquotedbl{}\#\textquotedbl{}-prefixed label. This footnote has a label so it can be referred to from multiple places, both as a footnote reference and as a hyperlink reference. -}, or symbolic\footnote{% +} +% +\DUfootnotetext{footnote-2}{footnote-reference-2}{3}{% +This footnote is numbered automatically and anonymously using a +label of \textquotedbl{}\#\textquotedbl{} only. +} +% +\DUfootnotetext{footnote-3}{footnote-reference-4}{*}{% Footnotes may also use symbols, specified with a \textquotedbl{}*\textquotedbl{} label. -}). +} +\end{quote} \end{document} -"""], -] +"""]]), + 'nested': ({}, [ + [r""" + It's possible to produce nested footnotes in LaTeX. [#]_ + + .. [#] It takes some work, though. [#]_ + .. [#] And don't even get me started on how tricky recursive footnotes would be. + """, + r"""\documentclass[a4paper]{article} +% generated by Docutils <https://docutils.sourceforge.io/> +\usepackage{cmap} % fix search and cut-and-paste in Acrobat +\usepackage[T1]{fontenc} + +%%% Custom LaTeX preamble +% PDF Standard Fonts +\usepackage{mathptmx} % Times +\usepackage[scaled=.90]{helvet} +\usepackage{courier} + +%%% User specified packages and stylesheets + +%%% Fallback definitions for Docutils-specific commands + +% numerical or symbol footnotes with hyperlinks and backlinks +\providecommand*{\DUfootnotemark}[3]{% + \raisebox{1em}{\hypertarget{#1}{}}% + \hyperlink{#2}{\textsuperscript{#3}}% +} +\providecommand{\DUfootnotetext}[4]{% + \begingroup% + \renewcommand{\thefootnote}{% + \protect\raisebox{1em}{\protect\hypertarget{#1}{}}% + \protect\hyperlink{#2}{#3}}% + \footnotetext{#4}% + \endgroup% +} -totest_latex_footnotes['nested'] = [ -# input -["""\ -It's possible to produce nested footnotes in LaTeX. [#]_ +% hyperlinks: +\ifdefined\hypersetup +\else + \usepackage[hyperfootnotes=false, + colorlinks=true,linkcolor=blue,urlcolor=blue]{hyperref} + \usepackage{bookmark} + \urlstyle{same} % normal text font (alternatives: tt, rm, sf) +\fi -.. [#] It takes some work, though. [#]_ -.. [#] And don't even get me started on how tricky recursive footnotes would be. -""", -## # expected output -head_template.substitute(dict(parts)) + r""" -It's possible to produce nested footnotes in LaTeX.\footnote{% -It takes some work, though.\footnotemark{} -}\footnotetext{% +%%% Body +\begin{document} + +\begin{quote} +It's possible to produce nested footnotes in LaTeX.\DUfootnotemark{footnote-reference-1}{footnote-1}{1} +% +\DUfootnotetext{footnote-1}{footnote-reference-1}{1}{% +It takes some work, though.\DUfootnotemark{footnote-reference-2}{footnote-2}{2} +} +% +\DUfootnotetext{footnote-2}{footnote-reference-2}{2}{% And don't even get me started on how tricky recursive footnotes would be. } +\end{quote} \end{document} -"""], -] +"""]]), + 'chained': ({}, [ + [r""" + It's possible to produce chained footnotes in LaTeX. [#]_ + + .. [#] They're just a special case of nested footnotes. [#]_ + .. [#] A nested footnote is a footnote on a footnote. [#]_ + .. [#] This is a footnote on a footnote on a footnote. + """, + r"""\documentclass[a4paper]{article} +% generated by Docutils <https://docutils.sourceforge.io/> +\usepackage{cmap} % fix search and cut-and-paste in Acrobat +\usepackage[T1]{fontenc} + +%%% Custom LaTeX preamble +% PDF Standard Fonts +\usepackage{mathptmx} % Times +\usepackage[scaled=.90]{helvet} +\usepackage{courier} + +%%% User specified packages and stylesheets + +%%% Fallback definitions for Docutils-specific commands + +% numerical or symbol footnotes with hyperlinks and backlinks +\providecommand*{\DUfootnotemark}[3]{% + \raisebox{1em}{\hypertarget{#1}{}}% + \hyperlink{#2}{\textsuperscript{#3}}% +} +\providecommand{\DUfootnotetext}[4]{% + \begingroup% + \renewcommand{\thefootnote}{% + \protect\raisebox{1em}{\protect\hypertarget{#1}{}}% + \protect\hyperlink{#2}{#3}}% + \footnotetext{#4}% + \endgroup% +} -totest_latex_footnotes['chained'] = [ -# input -["""\ -It's possible to produce chained footnotes in LaTeX. [#]_ +% hyperlinks: +\ifdefined\hypersetup +\else + \usepackage[hyperfootnotes=false, + colorlinks=true,linkcolor=blue,urlcolor=blue]{hyperref} + \usepackage{bookmark} + \urlstyle{same} % normal text font (alternatives: tt, rm, sf) +\fi -.. [#] They're just a special case of nested footnotes. [#]_ -.. [#] A nested footnote is a footnote on a footnote. [#]_ -.. [#] This is a footnote on a footnote on a footnote. -""", -## # expected output -head_template.substitute(dict(parts)) + r""" -It's possible to produce chained footnotes in LaTeX.\footnote{% -They're just a special case of nested footnotes.\footnotemark{} -}\footnotetext{% -A nested footnote is a footnote on a footnote.\footnotemark{} -}\footnotetext{% +%%% Body +\begin{document} + +\begin{quote} +It's possible to produce chained footnotes in LaTeX.\DUfootnotemark{footnote-reference-1}{footnote-1}{1} +% +\DUfootnotetext{footnote-1}{footnote-reference-1}{1}{% +They're just a special case of nested footnotes.\DUfootnotemark{footnote-reference-2}{footnote-2}{2} +} +% +\DUfootnotetext{footnote-2}{footnote-reference-2}{2}{% +A nested footnote is a footnote on a footnote.\DUfootnotemark{footnote-reference-3}{footnote-3}{3} +} +% +\DUfootnotetext{footnote-3}{footnote-reference-3}{3}{% This is a footnote on a footnote on a footnote. } +\end{quote} \end{document} -"""], -] - -totest_latex_footnotes['multinested'] = [ -# input -["""\ +"""]]), + 'multinested': ({}, [ + [r""" LaTeX isn't the best at nested footnote support. [#]_ .. [#] Specifically, it gets the numbers wrong [#]_ for "multinested" @@ -600,21 +758,67 @@ def test_body(self): show up as footnote 3. .. [#] That's a footnote that contains more than one footnote of its own. """, -## # expected output -head_template.substitute(dict(parts)) + r""" -LaTeX isn't the best at nested footnote support.\footnote{% -Specifically, it gets the numbers wrong\footnotemark{} for \textquotedbl{}multinested\textquotedbl{} -footnotes.\footnotemark{} -}\footnotetext{% +r"""\documentclass[a4paper]{article} +% generated by Docutils <https://docutils.sourceforge.io/> +\usepackage{cmap} % fix search and cut-and-paste in Acrobat +\usepackage[T1]{fontenc} + +%%% Custom LaTeX preamble +% PDF Standard Fonts +\usepackage{mathptmx} % Times +\usepackage[scaled=.90]{helvet} +\usepackage{courier} + +%%% User specified packages and stylesheets + +%%% Fallback definitions for Docutils-specific commands + +% numerical or symbol footnotes with hyperlinks and backlinks +\providecommand*{\DUfootnotemark}[3]{% + \raisebox{1em}{\hypertarget{#1}{}}% + \hyperlink{#2}{\textsuperscript{#3}}% +} +\providecommand{\DUfootnotetext}[4]{% + \begingroup% + \renewcommand{\thefootnote}{% + \protect\raisebox{1em}{\protect\hypertarget{#1}{}}% + \protect\hyperlink{#2}{#3}}% + \footnotetext{#4}% + \endgroup% +} + +% hyperlinks: +\ifdefined\hypersetup +\else + \usepackage[hyperfootnotes=false, + colorlinks=true,linkcolor=blue,urlcolor=blue]{hyperref} + \usepackage{bookmark} + \urlstyle{same} % normal text font (alternatives: tt, rm, sf) +\fi + +%%% Body +\begin{document} + +LaTeX isn't the best at nested footnote support.\DUfootnotemark{footnote-reference-1}{footnote-1}{1} +% +\DUfootnotetext{footnote-1}{footnote-reference-1}{1}{% +Specifically, it gets the numbers wrong\DUfootnotemark{footnote-reference-2}{footnote-2}{2} for \textquotedbl{}multinested\textquotedbl{} +footnotes.\DUfootnotemark{footnote-reference-3}{footnote-3}{3} +} +% +\DUfootnotetext{footnote-2}{footnote-reference-2}{2}{% For example, this should be footnote 2, but both it and the next one show up as footnote 3. -}\footnotetext{% +} +% +\DUfootnotetext{footnote-3}{footnote-reference-3}{3}{% That's a footnote that contains more than one footnote of its own. } \end{document} -"""], -] +"""]]), +} + if __name__ == '__main__': unittest.main() -- 2.50.1 |
From: Matěj C. <mc...@ce...> - 2025-07-22 00:16:19
|
From: John Thorvald Wodder II <gi...@va...> Attached is a patch that implements the --latex-footnotes option for 99% of use cases. I don't know whether you'd find it satisfactory enough to accept, but I thought I'd at least try. Shortcomings of this implementation: * Footnotes aren't hyperlinked back to their references. I am not aware of a way to solve this without basically reimplemeting docutils-footnotes. * Recursive footnotes are not supported and will cause a recursion error. Support would require tracking and referencing (à la https://tex.stackexchange.com/a/23158/) the number that LaTeX assigns to each footnote, which normally resets on chapters and would be broken by packages like footmisc and perpage. * If the same footnote is referenced multiple times, it will be treated as a new footnote each time. I believe this has the same solution as the above. * If a footnote contains two or more nested footnotes, the numbering will be messed up; see https://tex.stackexchange.com/q/38643/ for a way to address this. Originally: https://sourceforge.net/p/docutils/patches/182/ --- docutils/docutils/writers/latex2e/__init__.py | 91 +++++++++----- docutils/test/test_writers/test_latex2e.py | 116 ++++++++++++++++++ 2 files changed, 179 insertions(+), 28 deletions(-) diff --git a/docutils/docutils/writers/latex2e/__init__.py b/docutils/docutils/writers/latex2e/__init__.py index b8264ee7d..1dcf13466 100644 --- a/docutils/docutils/writers/latex2e/__init__.py +++ b/docutils/docutils/writers/latex2e/__init__.py @@ -227,14 +227,17 @@ class Writer(writers.Writer): {'dest': 'legacy_column_widths', 'action': 'store_false', 'validator': frontend.validate_boolean}), - # TODO: implement "latex footnotes" alternative - ('Footnotes with numbers/symbols by Docutils. (default) ' - '(The alternative, --latex-footnotes, is not implemented yet.)', + ('Footnotes with numbers/symbols by Docutils. (default)', ['--docutils-footnotes'], {'default': True, 'action': 'store_true', 'validator': frontend.validate_boolean}), ), + ('Footnotes with numbers by LaTeX.', + ['--latex-footnotes'], + {'dest': 'docutils_footnotes', + 'action': 'store_false', + 'validator': frontend.validate_boolean}), ) relative_path_settings = ('template',) @@ -1253,7 +1256,6 @@ def __init__(self, document, babel_class=Babel) -> None: else: self.graphicx_package = (r'\usepackage[%s]{graphicx}' % settings.graphicx_option) - # footnotes: TODO: implement LaTeX footnotes self.docutils_footnotes = settings.docutils_footnotes # Output collection stacks @@ -1319,6 +1321,15 @@ def __init__(self, document, babel_class=Babel) -> None: self.out = self.body self.out_stack = [] # stack of output collectors + # Texts of nested footnotes to emit once we finish the topmost + # footnote. footnote_queues[i] contains the text of footnotes + # encountered while processing the current footnote (which is nested + # within `i` higher footnotes). If i == 0, they will be emitted + # immediately after the current footnote ends; if i > 0; they will be + # added to footnote_queues[i-1] after ending the current footnote, + # which is added to the same queue before them. + self.footnote_queues = [] + # Process settings # ~~~~~~~~~~~~~~~~ # Encodings: @@ -2324,11 +2335,11 @@ def depart_footer(self, node) -> None: self.pop_output_collector() def visit_footnote(self, node) -> None: - try: - backref = node['backrefs'][0] - except IndexError: - backref = node['ids'][0] # no backref, use self-ref instead if self.docutils_footnotes: + try: + backref = node['backrefs'][0] + except IndexError: + backref = node['ids'][0] # no backref, use self-ref instead self.provide_fallback('footnotes') num = node[0].astext() if self.settings.footnote_references == 'brackets': @@ -2341,10 +2352,12 @@ def visit_footnote(self, node) -> None: # prevent spurious whitespace if footnote starts with paragraph: if len(node) > 1 and isinstance(node[1], nodes.paragraph): self.out.append('%') - # TODO: "real" LaTeX \footnote{}s (see visit_footnotes_reference()) + elif not self.footnote_queues: + raise nodes.SkipNode def depart_footnote(self, node) -> None: - self.out.append('}\n') + if self.docutils_footnotes: + self.out.append('}\n') def visit_footnote_reference(self, node) -> None: href = '' @@ -2352,25 +2365,47 @@ def visit_footnote_reference(self, node) -> None: href = node['refid'] elif 'refname' in node: href = self.document.nameids[node['refname']] - # if not self.docutils_footnotes: - # # TODO: insert footnote content at (or near) this place - # # see also docs/dev/todo.rst - # try: - # referenced_node = self.document.ids[node['refid']] - # except (AttributeError, KeyError): - # self.document.reporter.error( - # 'unresolved footnote-reference %s' % node) - # print('footnote-ref to %s' % referenced_node) - format = self.settings.footnote_references - if format == 'brackets': - self.append_hypertargets(node) - self.out.append('\\hyperlink{%s}{[' % href) - self.context.append(']}') + if self.docutils_footnotes: + format = self.settings.footnote_references + if format == 'brackets': + self.append_hypertargets(node) + self.out.append('\\hyperlink{%s}{[' % href) + self.context.append(']}') + else: + if not self.fallback_stylesheet: + self.fallbacks['footnotes'] = PreambleCmds.footnotes + self.out.append(r'\DUfootnotemark{%s}{%s}{' % + (node['ids'][0], href)) + self.context.append('}') else: - self.provide_fallback('footnotes') - self.out.append(r'\DUfootnotemark{%s}{%s}{' % - (node['ids'][0], href)) - self.context.append('}') + footnotes = (self.document.footnotes + + self.document.autofootnotes + + self.document.symbol_footnotes) + for footnote in footnotes: + if href in footnote['ids']: + self.footnote_queues.append([]) + self.push_output_collector([]) + footnote.walkabout(self) + text = ''.join(self.out) + self.pop_output_collector() + break + else: + self.document.reporter.error("Footnote %s referenced but not found" % href) + raise nodes.SkipNode + queued = self.footnote_queues.pop() + if not self.footnote_queues: + self.out.append("\\footnote{%") + self.out.append(text) + self.out.append("}") + for fn in queued: + self.out.append("\\footnotetext{%") + self.out.append(fn) + self.out.append("}") + else: + self.out.append("\\footnotemark{}") + self.footnote_queues[-1].append(text) + self.footnote_queues[-1].extend(queued) + raise nodes.SkipNode def depart_footnote_reference(self, node) -> None: self.out.append(self.context.pop()) diff --git a/docutils/test/test_writers/test_latex2e.py b/docutils/test/test_writers/test_latex2e.py index 84bb94e02..434786690 100755 --- a/docutils/test/test_writers/test_latex2e.py +++ b/docutils/test/test_writers/test_latex2e.py @@ -500,5 +500,121 @@ def test_body(self): ]) +totest_latex_footnotes['simple'] = [ +# input +["""\ +Paragraphs contain text and may contain footnote references (manually +numbered [1]_, anonymous auto-numbered [#]_, labeled auto-numbered +[#label]_, or symbolic [*]_). + +.. [1] A footnote contains body elements, consistently indented by at + least 3 spaces. + + This is the footnote's second paragraph. + +.. [#label] Footnotes may be numbered, either manually or + automatically using a "#"-prefixed label. This footnote has a + label so it can be referred to from multiple places, both as a + footnote reference and as a hyperlink reference. + +.. [#] This footnote is numbered automatically and anonymously using a + label of "#" only. + +.. [*] Footnotes may also use symbols, specified with a "*" label. +""", +## # expected output +head_template.substitute(dict(parts)) + r""" +Paragraphs contain text and may contain footnote references (manually +numbered\footnote{% +A footnote contains body elements, consistently indented by at +least 3 spaces. + +This is the footnote's second paragraph. +}, anonymous auto-numbered\footnote{% +This footnote is numbered automatically and anonymously using a +label of \textquotedbl{}\#\textquotedbl{} only. +}, labeled auto-numbered\footnote{% +Footnotes may be numbered, either manually or +automatically using a \textquotedbl{}\#\textquotedbl{}-prefixed label. This footnote has a +label so it can be referred to from multiple places, both as a +footnote reference and as a hyperlink reference. +}, or symbolic\footnote{% +Footnotes may also use symbols, specified with a \textquotedbl{}*\textquotedbl{} label. +}). + +\end{document} +"""], +] + +totest_latex_footnotes['nested'] = [ +# input +["""\ +It's possible to produce nested footnotes in LaTeX. [#]_ + +.. [#] It takes some work, though. [#]_ +.. [#] And don't even get me started on how tricky recursive footnotes would be. +""", +## # expected output +head_template.substitute(dict(parts)) + r""" +It's possible to produce nested footnotes in LaTeX.\footnote{% +It takes some work, though.\footnotemark{} +}\footnotetext{% +And don't even get me started on how tricky recursive footnotes would be. +} + +\end{document} +"""], +] + +totest_latex_footnotes['chained'] = [ +# input +["""\ +It's possible to produce chained footnotes in LaTeX. [#]_ + +.. [#] They're just a special case of nested footnotes. [#]_ +.. [#] A nested footnote is a footnote on a footnote. [#]_ +.. [#] This is a footnote on a footnote on a footnote. +""", +## # expected output +head_template.substitute(dict(parts)) + r""" +It's possible to produce chained footnotes in LaTeX.\footnote{% +They're just a special case of nested footnotes.\footnotemark{} +}\footnotetext{% +A nested footnote is a footnote on a footnote.\footnotemark{} +}\footnotetext{% +This is a footnote on a footnote on a footnote. +} + +\end{document} +"""], +] + +totest_latex_footnotes['multinested'] = [ +# input +["""\ +LaTeX isn't the best at nested footnote support. [#]_ + +.. [#] Specifically, it gets the numbers wrong [#]_ for "multinested" + footnotes. [#]_ +.. [#] For example, this should be footnote 2, but both it and the next one + show up as footnote 3. +.. [#] That's a footnote that contains more than one footnote of its own. +""", +## # expected output +head_template.substitute(dict(parts)) + r""" +LaTeX isn't the best at nested footnote support.\footnote{% +Specifically, it gets the numbers wrong\footnotemark{} for \textquotedbl{}multinested\textquotedbl{} +footnotes.\footnotemark{} +}\footnotetext{% +For example, this should be footnote 2, but both it and the next one +show up as footnote 3. +}\footnotetext{% +That's a footnote that contains more than one footnote of its own. +} + +\end{document} +"""], +] + if __name__ == '__main__': unittest.main() -- 2.50.1 |
From: Matěj C. <mc...@ce...> - 2025-07-22 00:16:22
Attachments:
signature.asc
E09FEF25D96484AC.asc
|
On Tue Jul 22, 2025 at 1:58 AM CEST, Matěj Cepl wrote: > [1] https://git.sr.ht/~mcepl/justin_susil/tree/master/item/p_just_2_apol.rst Resulting file https://git.sr.ht/~mcepl/justin_susil/tree/master/item/p_just_2_apol.tex looks reasonably well, only I am not certain about that EOL on the end of each footnote text. Any suggestions how to get rid of that? Best, Matěj -- http://matej.ceplovi.cz/blog/, @mc...@en...wn GPG Finger: 3C76 A027 CA45 AD70 98B5 BC1D 7920 5802 880B C9D8 My opinions may have changed, but not the fact that I am right. --Ashleigh Brilliant |
From: Matěj C. <mc...@ce...> - 2025-07-22 00:34:42
|
I was trying to be as conservative as possible (and given the length 1 of the suffix even .endswith() seemed like an overkill). --- docutils/docutils/writers/latex2e/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docutils/docutils/writers/latex2e/__init__.py b/docutils/docutils/writers/latex2e/__init__.py index 4b6b0b431..3c9b082e0 100644 --- a/docutils/docutils/writers/latex2e/__init__.py +++ b/docutils/docutils/writers/latex2e/__init__.py @@ -2387,6 +2387,8 @@ def visit_footnote_reference(self, node) -> None: self.push_output_collector([]) footnote.walkabout(self) text = ''.join(self.out) + if text[-1] == '\n': + text = text[:-1] self.pop_output_collector() break else: -- 2.50.1 |
From: Guenter M. <mi...@us...> - 2025-07-25 05:26:35
|
Hi Matěj, On 2025-07-21, Matěj Cepl wrote: ... > I found to my unpleasant surprise > that rst2xetex doesn’t produce correct LaTeX footnotes, but > something weird, which certainly cannot be controlled by the > standard LaTeX tools. > I have found this patch, and decided to at least rebase it > against the current master branch. I needed to fix tests, but > otherwise the test suite now seems to pass. > What do you think? Thanks for the patch to a long-standing TODO item. I'll look into it once the long overdue release 0.22 is out. The example article shows another problem: In HTML it is simple to use footnotes as endnotes. In LaTeX, we would like a different layout for endnotes vs. footnotes. (The current implementation only works (almost) OK, if the footnotes are placed immediately after the block-level element with the footnote reference.) BTW: In your article, footnotes like :: .. [#] t. j. M. Aurelia a Lucia Vera. are typeset "strange", because "t.", "j.", and "M." each start an enumerated list (loweralpha) so you get 3 nested lists with just one item "Aurelia a Lucia Vera". You may need to escape the letter or the dot, see the "Caution" admonition in https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#enumerated-lists OTOH, :: .. [#] t, j. jelikož Loga se dohledali a dovážili násilným hledáním a rozvažováním. does not become a list but I suspected that the comma is a typo. Günter |
From: Matěj C. <mc...@ce...> - 2025-07-25 07:25:54
Attachments:
signature.asc
E09FEF25D96484AC.asc
|
On Fri Jul 25, 2025 at 7:26 AM CEST, Guenter Milde via Docutils-develop wrote: > The example article shows another problem: > > In HTML it is simple to use footnotes as endnotes. In LaTeX, we would > like a different layout for endnotes vs. footnotes. (The current > implementation only works (almost) OK, if the footnotes are placed > immediately after the block-level element with the footnote reference.) I believe it just better to stick with the LaTeX solutions (e. g., [1]) rather than to generate something even more crazy. For HTML, it is IMHO quite enough the current way of having text of notes placed wherever you do so in the source ([2]). I see one more problem. When I put [xetex writer] latex_footnotes=True into ./docutils.conf, it doesn’t make any difference, I have to set `--latex-footnotes` on the command line. Any idea, where I failed to hook into the configuration system properly? > You may need to escape the letter or the dot, see the "Caution" admonition in > https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#enumerated-lists Thank you for that, it helped. Best, Matěj [1] https://tex.stackexchange.com/questions/56145/is-there-a-way-to-move-all-footnotes-to-the-end-of-the-document [2] https://matej.ceplovi.cz/blog/harry-potter-poznamky-jako-odpoved.html (source https://git.sr.ht/~mcepl/blog-source/tree/master/item/literature/harry-potter-odpoved-brachovi.rst) -- http://matej.ceplovi.cz/blog/, @mc...@en...wn GPG Finger: 3C76 A027 CA45 AD70 98B5 BC1D 7920 5802 880B C9D8 This is a signature anti-virus. Please stop the spread of signature viruses! |