mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Merge pull request #8832 from jfbu/latex_hyperlinked_caption_footnotes_on_3.x
Latex hyperlinked caption footnotes on 3.x
This commit is contained in:
commit
a164d4c12c
3
CHANGES
3
CHANGES
@ -118,6 +118,9 @@ Bugs fixed
|
||||
* #8683: :confval:`html_last_updated_fmt` generates wrong time zone for %Z
|
||||
* #1112: ``download`` role creates duplicated copies when relative path is
|
||||
specified
|
||||
* #2616 (fifth item): LaTeX: footnotes from captions are not clickable,
|
||||
and for manually numbered footnotes only first one with same number is
|
||||
an hyperlink
|
||||
* #7576: LaTeX with French babel and memoir crash: "Illegal parameter number
|
||||
in definition of ``\FNH@prefntext``"
|
||||
* #8055: LaTeX (docs): A potential display bug with the LaTeX generation step
|
||||
|
@ -325,8 +325,58 @@
|
||||
% some extras for Sphinx :
|
||||
% \sphinxfootnotemark: usable in section titles and silently removed from TOCs.
|
||||
\def\sphinxfootnotemark [#1]%
|
||||
{\ifx\thepage\relax\else\protect\spx@opt@BeforeFootnote
|
||||
\protect\footnotemark[#1]\fi}%
|
||||
{\ifx\thepage\relax\else\sphinxfootref{#1}\fi}%
|
||||
% \sphinxfootref:
|
||||
% - \spx@opt@BeforeFootnote is from BeforeFootnote sphinxsetup option
|
||||
% - \ref:
|
||||
% the latex.py writer inserts a \phantomsection\label{<scope>.<num>}
|
||||
% whenever
|
||||
% - the footnote was explicitly numbered in sources,
|
||||
% - or it was in restrained context and is rendered using footnotetext
|
||||
%
|
||||
% These are the two types of footnotes that \sphinxfootnotemark must
|
||||
% handle. But for explicitly numbered footnotes the same number
|
||||
% can be found in document. So a secondary part in <scope> is updated
|
||||
% at each novel such footnote to know what is the target from then on
|
||||
% for \sphinxfootnotemark and already encountered [1], or [2],...
|
||||
%
|
||||
% LaTeX package varioref is not supported by hyperref (from its doc: "There
|
||||
% are too many problems with varioref. Nobody has time to sort them out.
|
||||
% Therefore this package is now unsupported.") So we will simply use our own
|
||||
% macros to access the page number of footnote text and decide whether to print
|
||||
% it. \pagename is internationalized by latex-babel.
|
||||
\def\spx@thefnmark#1#2{%
|
||||
% #1=label for reference, #2=page where footnote was printed
|
||||
\ifx\spx@tempa\spx@tempb
|
||||
% same page
|
||||
#1%
|
||||
\else
|
||||
\sphinxthefootnotemark{#1}{#2}%
|
||||
\fi
|
||||
}%
|
||||
\def\sphinxfootref@get #1#2#3#4#5\relax{%
|
||||
\def\sphinxfootref@label{#1}%
|
||||
\def\sphinxfootref@page {#2}%
|
||||
\def\sphinxfootref@Href {#4}%
|
||||
}%
|
||||
\protected\def\sphinxfootref#1{% #1 always explicit number in Sphinx usage
|
||||
\spx@opt@BeforeFootnote
|
||||
\ltx@ifundefined{r@\thesphinxscope.#1}%
|
||||
{\gdef\@thefnmark{?}\H@@footnotemark}%
|
||||
{\expandafter\expandafter\expandafter\sphinxfootref@get
|
||||
\csname r@\thesphinxscope.#1\endcsname\relax
|
||||
\edef\spx@tempa{\thepage}\edef\spx@tempb{\sphinxfootref@page}%
|
||||
\protected@xdef\@thefnmark{\spx@thefnmark{\sphinxfootref@label}{\sphinxfootref@page}}%
|
||||
\let\spx@@makefnmark\@makefnmark
|
||||
\def\@makefnmark{%
|
||||
\hyper@linkstart{link}{\sphinxfootref@Href}%
|
||||
\spx@@makefnmark
|
||||
\hyper@linkend
|
||||
}%
|
||||
\H@@footnotemark
|
||||
\let\@makefnmark\spx@@makefnmark
|
||||
}%
|
||||
}%
|
||||
\AtBeginDocument{%
|
||||
% let hyperref less complain
|
||||
\pdfstringdefDisableCommands{\def\sphinxfootnotemark [#1]{}}%
|
||||
|
@ -847,6 +847,28 @@
|
||||
|
||||
%% FOOTNOTES
|
||||
%
|
||||
% Support scopes for footnote numbering
|
||||
\newcounter{sphinxscope}
|
||||
\newcommand{\sphinxstepscope}{\stepcounter{sphinxscope}}
|
||||
% Explictly numbered footnotes may be referred to, and for this to be
|
||||
% clickable we need to have only one target. So we will step this at each
|
||||
% explicit footnote and let \thesphinxscope take it into account
|
||||
\newcounter{sphinxexplicit}
|
||||
\newcommand{\sphinxstepexplicit}{\stepcounter{sphinxexplicit}}
|
||||
% Some babel/polyglossia languages fiddle with \@arabic, so let's be extra
|
||||
% cautious and redefine \thesphinxscope with \number not \@arabic.
|
||||
% Memo: we expect some subtle redefinition of \thesphinxscope to be a part of page
|
||||
% scoping for footnotes, when we shall implement it.
|
||||
\renewcommand{\thesphinxscope}{\number\value{sphinxscope}.\number\value{sphinxexplicit}}
|
||||
\newcommand\sphinxthefootnotemark[2]{%
|
||||
% this is used to make reference to an explicitly numbered footnote not on same page
|
||||
% #1=label of footnote text, #2=page number where footnote text was printed
|
||||
\ifdefined\pagename
|
||||
\pagename\space#2, % <- space
|
||||
\else
|
||||
p. #2, % <- space
|
||||
\fi #1% no space
|
||||
}
|
||||
% Support large numbered footnotes in minipage
|
||||
% But now obsolete due to systematic use of \savenotes/\spewnotes
|
||||
% when minipages are in use in the various macro definitions next.
|
||||
|
@ -846,11 +846,16 @@ class LaTeXTranslator(SphinxTranslator):
|
||||
def visit_footnote(self, node: Element) -> None:
|
||||
self.in_footnote += 1
|
||||
label = cast(nodes.label, node[0])
|
||||
if 'auto' not in node.attributes:
|
||||
self.body.append('\\sphinxstepexplicit ')
|
||||
if self.in_parsed_literal:
|
||||
self.body.append('\\begin{footnote}[%s]' % label.astext())
|
||||
else:
|
||||
self.body.append('%\n')
|
||||
self.body.append('\\begin{footnote}[%s]' % label.astext())
|
||||
if 'auto' not in node:
|
||||
self.body.append('\\phantomsection'
|
||||
'\\label{\\thesphinxscope.%s}%%\n' % label.astext())
|
||||
self.body.append('\\sphinxAtStartFootnote\n')
|
||||
|
||||
def depart_footnote(self, node: Element) -> None:
|
||||
@ -1749,7 +1754,9 @@ class LaTeXTranslator(SphinxTranslator):
|
||||
label = cast(nodes.label, node[0])
|
||||
self.body.append('%\n')
|
||||
self.body.append('\\begin{footnotetext}[%s]'
|
||||
'\\sphinxAtStartFootnote\n' % label.astext())
|
||||
'\\phantomsection\\label{\\thesphinxscope.%s}%%\n'
|
||||
% (label.astext(), label.astext()))
|
||||
self.body.append('\\sphinxAtStartFootnote\n')
|
||||
|
||||
def depart_footnotetext(self, node: Element) -> None:
|
||||
# the \ignorespaces in particular for after table header use
|
||||
|
@ -724,7 +724,8 @@ def test_footnote(app, status, warning):
|
||||
print(result)
|
||||
print(status.getvalue())
|
||||
print(warning.getvalue())
|
||||
assert ('\\begin{footnote}[1]\\sphinxAtStartFootnote\nnumbered\n%\n'
|
||||
assert ('\\sphinxstepexplicit %\n\\begin{footnote}[1]\\phantomsection'
|
||||
'\\label{\\thesphinxscope.1}%\n\\sphinxAtStartFootnote\nnumbered\n%\n'
|
||||
'\\end{footnote}') in result
|
||||
assert ('\\begin{footnote}[2]\\sphinxAtStartFootnote\nauto numbered\n%\n'
|
||||
'\\end{footnote}') in result
|
||||
@ -732,9 +733,13 @@ def test_footnote(app, status, warning):
|
||||
assert '\\sphinxcite{footnote:bar}' in result
|
||||
assert ('\\bibitem[bar]{footnote:bar}\n\\sphinxAtStartPar\ncite\n') in result
|
||||
assert '\\sphinxcaption{Table caption \\sphinxfootnotemark[4]' in result
|
||||
assert ('\\hline%\n\\begin{footnotetext}[4]\\sphinxAtStartFootnote\n'
|
||||
assert ('\\hline%\n\\begin{footnotetext}[4]'
|
||||
'\\phantomsection\\label{\\thesphinxscope.4}%\n'
|
||||
'\\sphinxAtStartFootnote\n'
|
||||
'footnote in table caption\n%\n\\end{footnotetext}\\ignorespaces %\n'
|
||||
'\\begin{footnotetext}[5]\\sphinxAtStartFootnote\n'
|
||||
'\\begin{footnotetext}[5]'
|
||||
'\\phantomsection\\label{\\thesphinxscope.5}%\n'
|
||||
'\\sphinxAtStartFootnote\n'
|
||||
'footnote in table header\n%\n\\end{footnotetext}\\ignorespaces '
|
||||
'\n\\sphinxAtStartPar\n'
|
||||
'VIDIOC\\_CROPCAP\n&\n\\sphinxAtStartPar\n') in result
|
||||
@ -760,19 +765,27 @@ def test_reference_in_caption_and_codeblock_in_footnote(app, status, warning):
|
||||
assert '\\subsubsection*{The rubric title with a reference to {[}AuthorYear{]}}' in result
|
||||
assert ('\\chapter{The section with a reference to \\sphinxfootnotemark[5]}\n'
|
||||
'\\label{\\detokenize{index:the-section-with-a-reference-to}}'
|
||||
'%\n\\begin{footnotetext}[5]\\sphinxAtStartFootnote\n'
|
||||
'%\n\\begin{footnotetext}[5]'
|
||||
'\\phantomsection\\label{\\thesphinxscope.5}%\n'
|
||||
'\\sphinxAtStartFootnote\n'
|
||||
'Footnote in section\n%\n\\end{footnotetext}') in result
|
||||
assert ('\\caption{This is the figure caption with a footnote to '
|
||||
'\\sphinxfootnotemark[7].}\\label{\\detokenize{index:id29}}\\end{figure}\n'
|
||||
'%\n\\begin{footnotetext}[7]\\sphinxAtStartFootnote\n'
|
||||
'%\n\\begin{footnotetext}[7]'
|
||||
'\\phantomsection\\label{\\thesphinxscope.7}%\n'
|
||||
'\\sphinxAtStartFootnote\n'
|
||||
'Footnote in caption\n%\n\\end{footnotetext}') in result
|
||||
assert ('\\sphinxcaption{footnote \\sphinxfootnotemark[8] in '
|
||||
'caption of normal table}\\label{\\detokenize{index:id30}}') in result
|
||||
assert ('\\caption{footnote \\sphinxfootnotemark[9] '
|
||||
'in caption \\sphinxfootnotemark[10] of longtable\\strut}') in result
|
||||
assert ('\\endlastfoot\n%\n\\begin{footnotetext}[9]\\sphinxAtStartFootnote\n'
|
||||
assert ('\\endlastfoot\n%\n\\begin{footnotetext}[9]'
|
||||
'\\phantomsection\\label{\\thesphinxscope.9}%\n'
|
||||
'\\sphinxAtStartFootnote\n'
|
||||
'Foot note in longtable\n%\n\\end{footnotetext}\\ignorespaces %\n'
|
||||
'\\begin{footnotetext}[10]\\sphinxAtStartFootnote\n'
|
||||
'\\begin{footnotetext}[10]'
|
||||
'\\phantomsection\\label{\\thesphinxscope.10}%\n'
|
||||
'\\sphinxAtStartFootnote\n'
|
||||
'Second footnote in caption of longtable\n') in result
|
||||
assert ('This is a reference to the code\\sphinxhyphen{}block in the footnote:\n'
|
||||
'{\\hyperref[\\detokenize{index:codeblockinfootnote}]'
|
||||
@ -793,7 +806,9 @@ def test_latex_show_urls_is_inline(app, status, warning):
|
||||
print(result)
|
||||
print(status.getvalue())
|
||||
print(warning.getvalue())
|
||||
assert ('Same footnote number %\n\\begin{footnote}[1]\\sphinxAtStartFootnote\n'
|
||||
assert ('Same footnote number \\sphinxstepexplicit %\n'
|
||||
'\\begin{footnote}[1]\\phantomsection\\label{\\thesphinxscope.1}%\n'
|
||||
'\\sphinxAtStartFootnote\n'
|
||||
'footnote in bar\n%\n\\end{footnote} in bar.rst') in result
|
||||
assert ('Auto footnote number %\n\\begin{footnote}[1]\\sphinxAtStartFootnote\n'
|
||||
'footnote in baz\n%\n\\end{footnote} in baz.rst') in result
|
||||
@ -807,12 +822,16 @@ def test_latex_show_urls_is_inline(app, status, warning):
|
||||
'{\\sphinxcrossref{The section with a reference to }}}' in result)
|
||||
assert ('First footnote: %\n\\begin{footnote}[2]\\sphinxAtStartFootnote\n'
|
||||
'First\n%\n\\end{footnote}') in result
|
||||
assert ('Second footnote: %\n\\begin{footnote}[1]\\sphinxAtStartFootnote\n'
|
||||
assert ('Second footnote: \\sphinxstepexplicit %\n'
|
||||
'\\begin{footnote}[1]\\phantomsection\\label{\\thesphinxscope.1}%\n'
|
||||
'\\sphinxAtStartFootnote\n'
|
||||
'Second\n%\n\\end{footnote}') in result
|
||||
assert '\\sphinxhref{http://sphinx-doc.org/}{Sphinx} (http://sphinx\\sphinxhyphen{}doc.org/)' in result
|
||||
assert ('Third footnote: %\n\\begin{footnote}[3]\\sphinxAtStartFootnote\n'
|
||||
'Third \\sphinxfootnotemark[4]\n%\n\\end{footnote}%\n'
|
||||
'\\begin{footnotetext}[4]\\sphinxAtStartFootnote\n'
|
||||
'\\begin{footnotetext}[4]'
|
||||
'\\phantomsection\\label{\\thesphinxscope.4}%\n'
|
||||
'\\sphinxAtStartFootnote\n'
|
||||
'Footnote inside footnote\n%\n\\end{footnotetext}\\ignorespaces') in result
|
||||
assert ('\\sphinxhref{http://sphinx-doc.org/~test/}{URL including tilde} '
|
||||
'(http://sphinx\\sphinxhyphen{}doc.org/\\textasciitilde{}test/)') in result
|
||||
@ -820,7 +839,9 @@ def test_latex_show_urls_is_inline(app, status, warning):
|
||||
'(http://sphinx\\sphinxhyphen{}doc.org/)}] '
|
||||
'\\leavevmode\n\\sphinxAtStartPar\nDescription' in result)
|
||||
assert ('\\item[{Footnote in term \\sphinxfootnotemark[6]}] '
|
||||
'\\leavevmode%\n\\begin{footnotetext}[6]\\sphinxAtStartFootnote\n'
|
||||
'\\leavevmode%\n\\begin{footnotetext}[6]'
|
||||
'\\phantomsection\\label{\\thesphinxscope.6}%\n'
|
||||
'\\sphinxAtStartFootnote\n'
|
||||
'Footnote in term\n%\n\\end{footnotetext}\\ignorespaces '
|
||||
'\n\\sphinxAtStartPar\nDescription') in result
|
||||
assert ('\\item[{\\sphinxhref{http://sphinx-doc.org/}{Term in deflist} '
|
||||
@ -841,7 +862,9 @@ def test_latex_show_urls_is_footnote(app, status, warning):
|
||||
print(result)
|
||||
print(status.getvalue())
|
||||
print(warning.getvalue())
|
||||
assert ('Same footnote number %\n\\begin{footnote}[1]\\sphinxAtStartFootnote\n'
|
||||
assert ('Same footnote number \\sphinxstepexplicit %\n'
|
||||
'\\begin{footnote}[1]\\phantomsection\\label{\\thesphinxscope.1}%\n'
|
||||
'\\sphinxAtStartFootnote\n'
|
||||
'footnote in bar\n%\n\\end{footnote} in bar.rst') in result
|
||||
assert ('Auto footnote number %\n\\begin{footnote}[2]\\sphinxAtStartFootnote\n'
|
||||
'footnote in baz\n%\n\\end{footnote} in baz.rst') in result
|
||||
@ -854,14 +877,18 @@ def test_latex_show_urls_is_footnote(app, status, warning):
|
||||
'{\\sphinxcrossref{The section with a reference to }}}') in result
|
||||
assert ('First footnote: %\n\\begin{footnote}[3]\\sphinxAtStartFootnote\n'
|
||||
'First\n%\n\\end{footnote}') in result
|
||||
assert ('Second footnote: %\n\\begin{footnote}[1]\\sphinxAtStartFootnote\n'
|
||||
assert ('Second footnote: \\sphinxstepexplicit %\n'
|
||||
'\\begin{footnote}[1]\\phantomsection\\label{\\thesphinxscope.1}%\n'
|
||||
'\\sphinxAtStartFootnote\n'
|
||||
'Second\n%\n\\end{footnote}') in result
|
||||
assert ('\\sphinxhref{http://sphinx-doc.org/}{Sphinx}'
|
||||
'%\n\\begin{footnote}[4]\\sphinxAtStartFootnote\n'
|
||||
'\\sphinxnolinkurl{http://sphinx-doc.org/}\n%\n\\end{footnote}') in result
|
||||
assert ('Third footnote: %\n\\begin{footnote}[6]\\sphinxAtStartFootnote\n'
|
||||
'Third \\sphinxfootnotemark[7]\n%\n\\end{footnote}%\n'
|
||||
'\\begin{footnotetext}[7]\\sphinxAtStartFootnote\n'
|
||||
'\\begin{footnotetext}[7]'
|
||||
'\\phantomsection\\label{\\thesphinxscope.7}%\n'
|
||||
'\\sphinxAtStartFootnote\n'
|
||||
'Footnote inside footnote\n%\n'
|
||||
'\\end{footnotetext}\\ignorespaces') in result
|
||||
assert ('\\sphinxhref{http://sphinx-doc.org/~test/}{URL including tilde}'
|
||||
@ -869,16 +896,22 @@ def test_latex_show_urls_is_footnote(app, status, warning):
|
||||
'\\sphinxnolinkurl{http://sphinx-doc.org/~test/}\n%\n\\end{footnote}') in result
|
||||
assert ('\\item[{\\sphinxhref{http://sphinx-doc.org/}'
|
||||
'{URL in term}\\sphinxfootnotemark[9]}] '
|
||||
'\\leavevmode%\n\\begin{footnotetext}[9]\\sphinxAtStartFootnote\n'
|
||||
'\\leavevmode%\n\\begin{footnotetext}[9]'
|
||||
'\\phantomsection\\label{\\thesphinxscope.9}%\n'
|
||||
'\\sphinxAtStartFootnote\n'
|
||||
'\\sphinxnolinkurl{http://sphinx-doc.org/}\n%\n'
|
||||
'\\end{footnotetext}\\ignorespaces \n\\sphinxAtStartPar\nDescription') in result
|
||||
assert ('\\item[{Footnote in term \\sphinxfootnotemark[11]}] '
|
||||
'\\leavevmode%\n\\begin{footnotetext}[11]\\sphinxAtStartFootnote\n'
|
||||
'\\leavevmode%\n\\begin{footnotetext}[11]'
|
||||
'\\phantomsection\\label{\\thesphinxscope.11}%\n'
|
||||
'\\sphinxAtStartFootnote\n'
|
||||
'Footnote in term\n%\n\\end{footnotetext}\\ignorespaces '
|
||||
'\n\\sphinxAtStartPar\nDescription') in result
|
||||
assert ('\\item[{\\sphinxhref{http://sphinx-doc.org/}{Term in deflist}'
|
||||
'\\sphinxfootnotemark[10]}] '
|
||||
'\\leavevmode%\n\\begin{footnotetext}[10]\\sphinxAtStartFootnote\n'
|
||||
'\\leavevmode%\n\\begin{footnotetext}[10]'
|
||||
'\\phantomsection\\label{\\thesphinxscope.10}%\n'
|
||||
'\\sphinxAtStartFootnote\n'
|
||||
'\\sphinxnolinkurl{http://sphinx-doc.org/}\n%\n'
|
||||
'\\end{footnotetext}\\ignorespaces \n\\sphinxAtStartPar\nDescription') in result
|
||||
assert ('\\sphinxurl{https://github.com/sphinx-doc/sphinx}\n' in result)
|
||||
@ -896,7 +929,9 @@ def test_latex_show_urls_is_no(app, status, warning):
|
||||
print(result)
|
||||
print(status.getvalue())
|
||||
print(warning.getvalue())
|
||||
assert ('Same footnote number %\n\\begin{footnote}[1]\\sphinxAtStartFootnote\n'
|
||||
assert ('Same footnote number \\sphinxstepexplicit %\n'
|
||||
'\\begin{footnote}[1]\\phantomsection\\label{\\thesphinxscope.1}%\n'
|
||||
'\\sphinxAtStartFootnote\n'
|
||||
'footnote in bar\n%\n\\end{footnote} in bar.rst') in result
|
||||
assert ('Auto footnote number %\n\\begin{footnote}[1]\\sphinxAtStartFootnote\n'
|
||||
'footnote in baz\n%\n\\end{footnote} in baz.rst') in result
|
||||
@ -909,18 +944,24 @@ def test_latex_show_urls_is_no(app, status, warning):
|
||||
'{\\sphinxcrossref{The section with a reference to }}}' in result)
|
||||
assert ('First footnote: %\n\\begin{footnote}[2]\\sphinxAtStartFootnote\n'
|
||||
'First\n%\n\\end{footnote}') in result
|
||||
assert ('Second footnote: %\n\\begin{footnote}[1]\\sphinxAtStartFootnote\n'
|
||||
assert ('Second footnote: \\sphinxstepexplicit %\n'
|
||||
'\\begin{footnote}[1]\\phantomsection\\label{\\thesphinxscope.1}%\n'
|
||||
'\\sphinxAtStartFootnote\n'
|
||||
'Second\n%\n\\end{footnote}') in result
|
||||
assert '\\sphinxhref{http://sphinx-doc.org/}{Sphinx}' in result
|
||||
assert ('Third footnote: %\n\\begin{footnote}[3]\\sphinxAtStartFootnote\n'
|
||||
'Third \\sphinxfootnotemark[4]\n%\n\\end{footnote}%\n'
|
||||
'\\begin{footnotetext}[4]\\sphinxAtStartFootnote\n'
|
||||
'\\begin{footnotetext}[4]'
|
||||
'\\phantomsection\\label{\\thesphinxscope.4}%\n'
|
||||
'\\sphinxAtStartFootnote\n'
|
||||
'Footnote inside footnote\n%\n\\end{footnotetext}\\ignorespaces') in result
|
||||
assert '\\sphinxhref{http://sphinx-doc.org/~test/}{URL including tilde}' in result
|
||||
assert ('\\item[{\\sphinxhref{http://sphinx-doc.org/}{URL in term}}] '
|
||||
'\\leavevmode\n\\sphinxAtStartPar\nDescription') in result
|
||||
assert ('\\item[{Footnote in term \\sphinxfootnotemark[6]}] '
|
||||
'\\leavevmode%\n\\begin{footnotetext}[6]\\sphinxAtStartFootnote\n'
|
||||
'\\leavevmode%\n\\begin{footnotetext}[6]'
|
||||
'\\phantomsection\\label{\\thesphinxscope.6}%\n'
|
||||
'\\sphinxAtStartFootnote\n'
|
||||
'Footnote in term\n%\n\\end{footnotetext}\\ignorespaces '
|
||||
'\n\\sphinxAtStartPar\nDescription') in result
|
||||
assert ('\\item[{\\sphinxhref{http://sphinx-doc.org/}{Term in deflist}}] '
|
||||
|
Loading…
Reference in New Issue
Block a user