LaTeX: Inhibit breaks for rows with merged vertical cells (#14227)

This commit is contained in:
Tim Nordell
2025-12-30 23:46:12 +01:00
committed by GitHub
parent 3c85411fd0
commit 20f1c46790
9 changed files with 338 additions and 12 deletions
+2
View File
@@ -35,6 +35,8 @@ Bugs fixed
Patch by Günter Milde
* LaTeX: Fix rendering for grid filled merged vertical cell.
Patch by Tim Nordell
* #14228: LaTeX: Fix overrun footer for cases of merged vertical table cells.
Patch by Tim Nordell
* #14207: Fix creating ``HTMLThemeFactory`` objects in third-party extensions.
Patch by Adam Turner
* #3099: LaTeX: PDF build crashes if a code-block contains more than
+4 -3
View File
@@ -1,7 +1,7 @@
%% TABLES (WITH SUPPORT FOR MERGED CELLS OF GENERAL CONTENTS)
%
% change this info string if making any custom modification
\ProvidesPackage{sphinxlatextables}[2025/06/30 v9.0.0 tables]%
\ProvidesPackage{sphinxlatextables}[2025/12/30 v9.1.0 tables]%
% Provides support for this output mark-up from Sphinx latex writer
% and table templates:
@@ -776,7 +776,7 @@
\leaders\hrule\@height\arrayrulewidth\hfill}%
\cr
% the last one will need to be compensated, this is job of \sphinxclines
\noalign{\vskip-\arrayrulewidth}%
\noalign{\nobreak\vskip-\arrayrulewidth}%
}
\def\spx@table@fixvlinejoin{%
{\CT@arc@ % this is the color command set up by \arrayrulecolor
@@ -800,7 +800,7 @@
\hfill
\spx@table@fixvlinejoin
\cr
\noalign{\vskip-\arrayrulewidth}%
\noalign{\nobreak\vskip-\arrayrulewidth}%
}
% This "fixclines" is also needed if no \sphinxcline emitted and is useful
% even in extreme case with no \sphinxvlinecrossing either, to give correct
@@ -819,6 +819,7 @@
\spx@table@fixvlinejoin% fill small gap at right-bordered table
\cr
% this final one does NO \vskip-\arrayrulewidth... that's the whole point
\noalign{\nobreak}%
}
%%%% end of \cline workarounds
+2 -1
View File
@@ -1307,15 +1307,16 @@ class LaTeXTranslator(SphinxTranslator):
def depart_row(self, node: Element) -> None:
assert self.table is not None
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 # type: ignore[union-attr]
for cell in cells
]
if all(underlined):
self.body.append(r'\\' + CR)
self.body.append(r'\sphinxhline')
else:
self.body.append(r'\\*' + CR)
i = 0
underlined.extend([False]) # sentinel
if underlined[0] is False:
@@ -52,7 +52,7 @@ cell1\sphinxhyphen{}5
\sphinxbeforeendvarwidth
\end{varwidth}%
}%
\\
\\*
\sphinxvlinecrossing{1}\sphinxcline{3-3}\sphinxvlinecrossing{4}\sphinxfixclines{5}\sphinxtablestrut{1}&\sphinxtablestrut{2}&\sphinxmultirow{2}{6}{%
\begin{varwidth}[t]{\sphinxcolwidth{1}{5}}
\sphinxAtStartPar
@@ -60,7 +60,7 @@ cell2\sphinxhyphen{}3
\sphinxbeforeendvarwidth
\end{varwidth}%
}%
&\sphinxtablestrut{4}&\sphinxtablestrut{5}\\
&\sphinxtablestrut{4}&\sphinxtablestrut{5}\\*
\sphinxvlinecrossing{1}\sphinxvlinecrossing{2}\sphinxvlinecrossing{3}\sphinxcline{5-5}\sphinxfixclines{5}\sphinxtablestrut{1}&\sphinxtablestrut{2}&\sphinxtablestrut{6}&\sphinxtablestrut{4}&\begin{varwidth}[t]{\sphinxcolwidth{1}{5}}
\sphinxAtStartPar
cell3\sphinxhyphen{}5
@@ -40,7 +40,7 @@ cell1\sphinxhyphen{}2
cell1\sphinxhyphen{}3
\sphinxbeforeendvarwidth
\end{varwidth}%
\\
\\*
\sphinxcline{1-1}\sphinxcline{3-3}\sphinxfixclines{3}\sphinxmultirow{2}{7}{%
\begin{varwidth}[t]{\sphinxcolwidth{1}{3}}
\sphinxAtStartPar
@@ -53,7 +53,7 @@ cell2\sphinxhyphen{}1
cell2\sphinxhyphen{}3
\sphinxbeforeendvarwidth
\end{varwidth}%
\\
\\*
\sphinxcline{2-3}\sphinxfixclines{3}\sphinxtablestrut{7}&\sphinxstartmulticolumn{2}%
\sphinxmultirow{2}{9}{%
\begin{varwidth}[t]{\sphinxcolwidth{2}{3}}
@@ -66,7 +66,7 @@ cell3\sphinxhyphen{}2\sphinxhyphen{}par2
\end{varwidth}%
}%
\sphinxstopmulticolumn
\\
\\*
\sphinxcline{1-1}\sphinxfixclines{3}\begin{varwidth}[t]{\sphinxcolwidth{1}{3}}
\sphinxAtStartPar
cell4\sphinxhyphen{}1
@@ -40,7 +40,7 @@ cell1\sphinxhyphen{}2
cell1\sphinxhyphen{}3
\sphinxbeforeendvarwidth
\end{varwidth}%
\\
\\*
\sphinxcline{1-1}\sphinxcline{3-3}\sphinxfixclines{3}\sphinxmultirow{2}{7}{%
\begin{varwidth}[t]{\sphinxcolwidth{1}{3}}
\sphinxAtStartPar
@@ -53,7 +53,7 @@ cell2\sphinxhyphen{}1
cell2\sphinxhyphen{}3
\sphinxbeforeendvarwidth
\end{varwidth}%
\\
\\*
\sphinxcline{2-3}\sphinxfixclines{3}\sphinxtablestrut{7}&\sphinxstartmulticolumn{2}%
\sphinxmultirow{2}{9}{%
\begin{varwidth}[t]{\sphinxcolwidth{2}{3}}
@@ -66,7 +66,7 @@ cell3\sphinxhyphen{}2\sphinxhyphen{}par2
\end{varwidth}%
}%
\sphinxstopmulticolumn
\\
\\*
\sphinxcline{1-1}\sphinxfixclines{3}\begin{varwidth}[t]{\sphinxcolwidth{1}{3}}
\sphinxAtStartPar
cell4\sphinxhyphen{}1
@@ -0,0 +1,250 @@
\label{\detokenize{longtable:longtable-with-multirow-cell-near-page-footer}}
\sphinxAtStartPar
table having …
\begin{itemize}
\item {}
\sphinxAtStartPar
consecutive multirow near footer
\end{itemize}
\newpage
\vspace*{\dimeval{\textheight - 5\baselineskip}}
\begin{savenotes}
\sphinxatlongtablestart
\sphinxthistablewithglobalstyle
\makeatletter
\LTleft \@totalleftmargin plus1fill
\LTright\dimexpr\columnwidth-\@totalleftmargin-\linewidth\relax plus1fill
\makeatother
\begin{longtable}{|l|l|}
\sphinxtoprule
\endfirsthead
\multicolumn{2}{c}{\sphinxnorowcolor
\makebox[0pt]{\sphinxtablecontinued{\tablename\ \thetable{} \textendash{} continued from previous page}}%
}\\
\sphinxtoprule
\endhead
\sphinxbottomrule
\multicolumn{2}{r}{\sphinxnorowcolor
\makebox[0pt][r]{\sphinxtablecontinued{continues on next page}}%
}\\
\endfoot
\endlastfoot
\sphinxtableatstartofbodyhook
\sphinxmultirow{16}{1}{%
\begin{varwidth}[t]{\sphinxcolwidth{1}{2}}
\sphinxAtStartPar
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex
sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis
convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus
fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada
lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti
sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
\sphinxbeforeendvarwidth
\end{varwidth}%
}%
&\begin{varwidth}[t]{\sphinxcolwidth{1}{2}}
\sphinxAtStartPar
Cell 1
\sphinxbeforeendvarwidth
\end{varwidth}%
\\*
\sphinxcline{2-2}\sphinxfixclines{2}\sphinxtablestrut{1}&\begin{varwidth}[t]{\sphinxcolwidth{1}{2}}
\sphinxAtStartPar
Cell 2
\sphinxbeforeendvarwidth
\end{varwidth}%
\\*
\sphinxcline{2-2}\sphinxfixclines{2}\sphinxtablestrut{1}&\begin{varwidth}[t]{\sphinxcolwidth{1}{2}}
\sphinxAtStartPar
Cell 3
\sphinxbeforeendvarwidth
\end{varwidth}%
\\*
\sphinxcline{2-2}\sphinxfixclines{2}\sphinxtablestrut{1}&\begin{varwidth}[t]{\sphinxcolwidth{1}{2}}
\sphinxAtStartPar
Cell 4
\sphinxbeforeendvarwidth
\end{varwidth}%
\\*
\sphinxcline{2-2}\sphinxfixclines{2}\sphinxtablestrut{1}&\begin{varwidth}[t]{\sphinxcolwidth{1}{2}}
\sphinxAtStartPar
Cell 5
\sphinxbeforeendvarwidth
\end{varwidth}%
\\*
\sphinxcline{2-2}\sphinxfixclines{2}\sphinxtablestrut{1}&\begin{varwidth}[t]{\sphinxcolwidth{1}{2}}
\sphinxAtStartPar
Cell 6
\sphinxbeforeendvarwidth
\end{varwidth}%
\\*
\sphinxcline{2-2}\sphinxfixclines{2}\sphinxtablestrut{1}&\begin{varwidth}[t]{\sphinxcolwidth{1}{2}}
\sphinxAtStartPar
Cell 7
\sphinxbeforeendvarwidth
\end{varwidth}%
\\*
\sphinxcline{2-2}\sphinxfixclines{2}\sphinxtablestrut{1}&\begin{varwidth}[t]{\sphinxcolwidth{1}{2}}
\sphinxAtStartPar
Cell 8
\sphinxbeforeendvarwidth
\end{varwidth}%
\\*
\sphinxcline{2-2}\sphinxfixclines{2}\sphinxtablestrut{1}&\begin{varwidth}[t]{\sphinxcolwidth{1}{2}}
\sphinxAtStartPar
Cell 9
\sphinxbeforeendvarwidth
\end{varwidth}%
\\*
\sphinxcline{2-2}\sphinxfixclines{2}\sphinxtablestrut{1}&\begin{varwidth}[t]{\sphinxcolwidth{1}{2}}
\sphinxAtStartPar
Cell 10
\sphinxbeforeendvarwidth
\end{varwidth}%
\\*
\sphinxcline{2-2}\sphinxfixclines{2}\sphinxtablestrut{1}&\begin{varwidth}[t]{\sphinxcolwidth{1}{2}}
\sphinxAtStartPar
Cell 11
\sphinxbeforeendvarwidth
\end{varwidth}%
\\*
\sphinxcline{2-2}\sphinxfixclines{2}\sphinxtablestrut{1}&\begin{varwidth}[t]{\sphinxcolwidth{1}{2}}
\sphinxAtStartPar
Cell 12
\sphinxbeforeendvarwidth
\end{varwidth}%
\\*
\sphinxcline{2-2}\sphinxfixclines{2}\sphinxtablestrut{1}&\begin{varwidth}[t]{\sphinxcolwidth{1}{2}}
\sphinxAtStartPar
Cell 13
\sphinxbeforeendvarwidth
\end{varwidth}%
\\*
\sphinxcline{2-2}\sphinxfixclines{2}\sphinxtablestrut{1}&\begin{varwidth}[t]{\sphinxcolwidth{1}{2}}
\sphinxAtStartPar
Cell 14
\sphinxbeforeendvarwidth
\end{varwidth}%
\\*
\sphinxcline{2-2}\sphinxfixclines{2}\sphinxtablestrut{1}&\begin{varwidth}[t]{\sphinxcolwidth{1}{2}}
\sphinxAtStartPar
Cell 15
\sphinxbeforeendvarwidth
\end{varwidth}%
\\*
\sphinxcline{2-2}\sphinxfixclines{2}\sphinxtablestrut{1}&\begin{varwidth}[t]{\sphinxcolwidth{1}{2}}
\sphinxAtStartPar
Cell 16
\sphinxbeforeendvarwidth
\end{varwidth}%
\\
\sphinxhline\begin{varwidth}[t]{\sphinxcolwidth{1}{2}}
\sphinxAtStartPar
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex
sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis
convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus
fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada
lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti
sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
\sphinxbeforeendvarwidth
\end{varwidth}%
&\begin{varwidth}[t]{\sphinxcolwidth{1}{2}}
\sphinxbeforeendvarwidth
\end{varwidth}%
\\
\sphinxhline\begin{varwidth}[t]{\sphinxcolwidth{1}{2}}
\sphinxAtStartPar
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex
sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis
convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus
fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada
lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti
sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
\sphinxbeforeendvarwidth
\end{varwidth}%
&\begin{varwidth}[t]{\sphinxcolwidth{1}{2}}
\sphinxbeforeendvarwidth
\end{varwidth}%
\\
\sphinxhline\begin{varwidth}[t]{\sphinxcolwidth{1}{2}}
\sphinxAtStartPar
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex
sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis
convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus
fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada
lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti
sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
\sphinxbeforeendvarwidth
\end{varwidth}%
&\begin{varwidth}[t]{\sphinxcolwidth{1}{2}}
\sphinxbeforeendvarwidth
\end{varwidth}%
\\
\sphinxhline\begin{varwidth}[t]{\sphinxcolwidth{1}{2}}
\sphinxAtStartPar
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex
sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis
convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus
fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada
lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti
sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
\sphinxbeforeendvarwidth
\end{varwidth}%
&\begin{varwidth}[t]{\sphinxcolwidth{1}{2}}
\sphinxbeforeendvarwidth
\end{varwidth}%
\\
\sphinxhline\begin{varwidth}[t]{\sphinxcolwidth{1}{2}}
\sphinxAtStartPar
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex
sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis
convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus
fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada
lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti
sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
\sphinxbeforeendvarwidth
\end{varwidth}%
&\begin{varwidth}[t]{\sphinxcolwidth{1}{2}}
\sphinxbeforeendvarwidth
\end{varwidth}%
\\
\sphinxhline\begin{varwidth}[t]{\sphinxcolwidth{1}{2}}
\sphinxAtStartPar
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex
sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis
convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus
fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada
lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti
sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
\sphinxbeforeendvarwidth
\end{varwidth}%
&\begin{varwidth}[t]{\sphinxcolwidth{1}{2}}
\sphinxbeforeendvarwidth
\end{varwidth}%
\\
\sphinxhline\begin{varwidth}[t]{\sphinxcolwidth{1}{2}}
\sphinxAtStartPar
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex
sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis
convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus
fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada
lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti
sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
\sphinxbeforeendvarwidth
\end{varwidth}%
&\begin{varwidth}[t]{\sphinxcolwidth{1}{2}}
\sphinxbeforeendvarwidth
\end{varwidth}%
\\
\sphinxbottomrule
\end{longtable}
\sphinxtableafterendhook
\sphinxatlongtableend
\end{savenotes}
@@ -154,3 +154,74 @@ longtable having stub columns and formerly problematic
* - cell2-1
- cell2-2
- cell2-3
longtable with multirow cell near page footer
---------------------------------------------
.. |lorem| replace::
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex
sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis
convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus
fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada
lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti
sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
table having ...
* consecutive multirow near footer
.. raw:: latex
\newpage
\vspace*{\dimeval{\textheight - 5\baselineskip}}
.. rst-class:: longtable
+-----------+-----------+
| |lorem| | Cell 1 |
| +-----------+
| | Cell 2 |
| +-----------+
| | Cell 3 |
| +-----------+
| | Cell 4 |
| +-----------+
| | Cell 5 |
| +-----------+
| | Cell 6 |
| +-----------+
| | Cell 7 |
| +-----------+
| | Cell 8 |
| +-----------+
| | Cell 9 |
| +-----------+
| | Cell 10 |
| +-----------+
| | Cell 11 |
| +-----------+
| | Cell 12 |
| +-----------+
| | Cell 13 |
| +-----------+
| | Cell 14 |
| +-----------+
| | Cell 15 |
| +-----------+
| | Cell 16 |
+-----------+-----------+
| |lorem| | |
+-----------+-----------+
| |lorem| | |
+-----------+-----------+
| |lorem| | |
+-----------+-----------+
| |lorem| | |
+-----------+-----------+
| |lorem| | |
+-----------+-----------+
| |lorem| | |
+-----------+-----------+
| |lorem| | |
+-----------+-----------+
+1
View File
@@ -1673,6 +1673,7 @@ def test_latex_table_longtable(app: SphinxTestApp) -> None:
'longtable having formerly problematic',
'longtable having widths and formerly problematic',
'longtable having stub columns and formerly problematic',
'longtable with multirow cell near page footer',
):
actual = tables[sectname]
expected = get_expected(sectname.replace(' ', '_'))