Skip to content

Commit 152b783

Browse files
jfbuAA-Turner
andauthored
LaTeX: fix figures in seealso and seealso inside table cell (#12598)
This also fixes unreported issues unrelated to the 7.4.0 release: - (now old style "lightbox") admonitions render badly in tabulary, - "heavybox" admonitions (they are all now but warning et al. were already) cause PDF crash if in tabulary and rendered badly if in tabular or longtable. - figure in a table cell is not properly separated from immediately preceding text. Co-authored-by: Adam Turner <[email protected]>
1 parent 5edca97 commit 152b783

File tree

8 files changed

+62
-6
lines changed

8 files changed

+62
-6
lines changed

CHANGES.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ Bugs fixed
88
values to a list.
99
Log an error message when string values are detected.
1010
Patch by Adam Turner.
11+
* #12594: LaTeX: since 7.4.0, :rst:dir:`seealso` and other "light" admonitions
12+
now break PDF builds if they contain a :dudir:`figure` directive; and also
13+
if they are contained in a table cell (rendered by ``tabulary``).
14+
Patch by Jean-François B.
1115

1216
Release 7.4.4 (released Jul 15, 2024)
1317
=====================================

sphinx/ext/todo.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,13 +215,17 @@ def latex_visit_todo_node(self: LaTeXTranslator, node: todo_node) -> None:
215215
title_node = cast(nodes.title, node[0])
216216
title = texescape.escape(title_node.astext(), self.config.latex_engine)
217217
self.body.append('%s:}' % title)
218+
self.no_latex_floats += 1
219+
if self.table:
220+
self.table.has_problematic = True
218221
node.pop(0)
219222
else:
220223
raise nodes.SkipNode
221224

222225

223226
def latex_depart_todo_node(self: LaTeXTranslator, node: todo_node) -> None:
224227
self.body.append('\\end{sphinxtodo}\n')
228+
self.no_latex_floats -= 1
225229

226230

227231
def setup(app: Sphinx) -> ExtensionMetadata:

sphinx/texinputs/sphinxlatextables.sty

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@
108108
\vbox{}% get correct baseline from above
109109
\LTpre\z@skip\LTpost\z@skip % set to zero longtable's own skips
110110
\edef\sphinxbaselineskip{\dimexpr\the\dimexpr\baselineskip\relax\relax}%
111+
\spx@inframedtrue % message to sphinxheavybox
111112
}%
112113
% Compatibility with caption package
113114
\def\sphinxthelongtablecaptionisattop{%
@@ -121,7 +122,9 @@
121122
\def\sphinxatlongtableend{\@nobreakfalse % latex3/latex2e#173
122123
\prevdepth\z@\vskip\sphinxtablepost\relax}%
123124
% B. Table with tabular or tabulary
124-
\def\sphinxattablestart{\par\vskip\dimexpr\sphinxtablepre\relax}%
125+
\def\sphinxattablestart{\par\vskip\dimexpr\sphinxtablepre\relax
126+
\spx@inframedtrue % message to sphinxheavybox
127+
}%
125128
\let\sphinxattableend\sphinxatlongtableend
126129
% This is used by tabular and tabulary templates
127130
\newcommand*\sphinxcapstartof[1]{%

sphinx/writers/latex.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,7 @@ def __init__(self, document: nodes.document, builder: LaTeXBuilder,
306306
self.in_term = 0
307307
self.needs_linetrimming = 0
308308
self.in_minipage = 0
309+
# only used by figure inside an admonition
309310
self.no_latex_floats = 0
310311
self.first_document = 1
311312
self.this_is_the_title = 1
@@ -966,11 +967,15 @@ def depart_desc_annotation(self, node: Element) -> None:
966967
def visit_seealso(self, node: Element) -> None:
967968
self.body.append(BLANKLINE)
968969
self.body.append(r'\begin{sphinxseealso}{%s:}' % admonitionlabels['seealso'] + CR)
970+
self.no_latex_floats += 1
971+
if self.table:
972+
self.table.has_problematic = True
969973

970974
def depart_seealso(self, node: Element) -> None:
971975
self.body.append(BLANKLINE)
972976
self.body.append(r'\end{sphinxseealso}')
973977
self.body.append(BLANKLINE)
978+
self.no_latex_floats -= 1
974979

975980
def visit_rubric(self, node: nodes.rubric) -> None:
976981
if len(node) == 1 and node.astext() in ('Footnotes', _('Footnotes')):
@@ -1512,6 +1517,8 @@ def visit_figure(self, node: Element) -> None:
15121517
if self.no_latex_floats:
15131518
align = "H"
15141519
if self.table:
1520+
# Blank line is needed if text precedes
1521+
self.body.append(BLANKLINE)
15151522
# TODO: support align option
15161523
if 'width' in node:
15171524
length = self.latex_image_length(node['width'])
@@ -1580,6 +1587,8 @@ def depart_legend(self, node: Element) -> None:
15801587
def visit_admonition(self, node: Element) -> None:
15811588
self.body.append(CR + r'\begin{sphinxadmonition}{note}')
15821589
self.no_latex_floats += 1
1590+
if self.table:
1591+
self.table.has_problematic = True
15831592

15841593
def depart_admonition(self, node: Element) -> None:
15851594
self.body.append(r'\end{sphinxadmonition}' + CR)
@@ -1590,6 +1599,8 @@ def _visit_named_admonition(self, node: Element) -> None:
15901599
self.body.append(CR + r'\begin{sphinxadmonition}{%s}{%s:}' %
15911600
(node.tagname, label))
15921601
self.no_latex_floats += 1
1602+
if self.table:
1603+
self.table.has_problematic = True
15931604

15941605
def _depart_named_admonition(self, node: Element) -> None:
15951606
self.body.append(r'\end{sphinxadmonition}' + CR)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
1+
extensions = ['sphinx.ext.todo']
2+
todo_include_todos = True
13
exclude_patterns = ['_build']

tests/roots/test-latex-figure-in-admonition/index.rst

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,24 @@ Test Figure in Admonition
33

44
.. caution::
55

6-
This uses a figure in an admonition.
6+
This uses a figure in a caution directive.
77

88
.. figure:: img.png
99

10+
.. note::
11+
12+
This uses a figure in a note directive.
13+
14+
.. figure:: img.png
15+
16+
.. seealso::
17+
18+
This uses a figure in a seealso directive.
19+
20+
.. figure:: img.png
21+
22+
.. todo::
23+
24+
This uses a figure in a todo directive.
25+
26+
.. figure:: img.png

tests/roots/test-root/markup.txt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,19 @@ Tables with multirow and multicol:
230230

231231
figure in table
232232

233+
* - .. warning::
234+
235+
warning in table
236+
237+
* - .. seealso::
238+
239+
figure in a seealso in a table
240+
241+
.. figure:: img.png
242+
243+
with a caption
244+
245+
and a legend
233246

234247
Figures
235248
-------

tests/test_builders/test_build_latex.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -158,21 +158,21 @@ def test_writer(app, status, warning):
158158

159159
assert ('\\begin{wrapfigure}{r}{0pt}\n\\centering\n'
160160
'\\noindent\\sphinxincludegraphics{{rimg}.png}\n'
161-
'\\caption{figure with align option}\\label{\\detokenize{markup:id9}}'
161+
'\\caption{figure with align option}\\label{\\detokenize{markup:id10}}'
162162
'\\end{wrapfigure}\n\n'
163163
'\\mbox{}\\par\\vskip-\\dimexpr\\baselineskip+\\parskip\\relax' in result)
164164

165165
assert ('\\begin{wrapfigure}{r}{0.500\\linewidth}\n\\centering\n'
166166
'\\noindent\\sphinxincludegraphics{{rimg}.png}\n'
167167
'\\caption{figure with align \\& figwidth option}'
168-
'\\label{\\detokenize{markup:id10}}'
168+
'\\label{\\detokenize{markup:id11}}'
169169
'\\end{wrapfigure}\n\n'
170170
'\\mbox{}\\par\\vskip-\\dimexpr\\baselineskip+\\parskip\\relax' in result)
171171

172172
assert ('\\begin{wrapfigure}{r}{3cm}\n\\centering\n'
173173
'\\noindent\\sphinxincludegraphics[width=3cm]{{rimg}.png}\n'
174174
'\\caption{figure with align \\& width option}'
175-
'\\label{\\detokenize{markup:id11}}'
175+
'\\label{\\detokenize{markup:id12}}'
176176
'\\end{wrapfigure}\n\n'
177177
'\\mbox{}\\par\\vskip-\\dimexpr\\baselineskip+\\parskip\\relax' in result)
178178

@@ -1591,7 +1591,9 @@ def test_latex_labels(app, status, warning):
15911591
def test_latex_figure_in_admonition(app, status, warning):
15921592
app.build(force_all=True)
15931593
result = (app.outdir / 'projectnamenotset.tex').read_text(encoding='utf8')
1594-
assert r'\begin{figure}[H]' in result
1594+
assert 'tabulary' not in result
1595+
for type in ('caution', 'note', 'seealso', 'todo'):
1596+
assert f'{type} directive.\n\n\\begin{{figure}}[H]' in result
15951597

15961598

15971599
def test_default_latex_documents():

0 commit comments

Comments
 (0)