mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Improve latex tables: fix #1574 and other rendering issues
- allow multi-paragraph contents in grid table merged cells - allow code-blocks in merged cells - allow generally speaking reST contents allowed in regular cells to be also allowed in merged cells, whether multirow, multicolumn, or both. This is made possible by custom LaTeX macros replacing original ``\multicolumn`` and ``\multirow`` (none of the originals allows verbatim contents as is needed for code-blocks). They are defined in bundled LaTeX style file sphinxmulticell.sty. The multicolumn merged cells give much better results with tabulary as it is coerced into taking them into account in its automatic width algorithm. This deprecates use of LaTeX packages eqparbox and multirow, which are not needed anymore. New config setting ``latex_use_latex_multicolumn`` (default value False, currently) as custom Sphinx multicolumn is not fully compatible will all types of custom table col specs which may be inserted via tabularcolumns directive. It works best with standard ``|`` column separator. The default tabulary column specifier has been changed from L (flushleft) to J (justifying). Internally the column type is called T, so ``r'\newcolumntype{T}{L}'`` in preamble key recovers the former behaviour. A ``\Y`` column type is defined which admits one decimal argument in place of the two integers for ``\X``.
This commit is contained in:
parent
a4d1fb7dec
commit
bc67d4298f
@ -1623,6 +1623,22 @@ These options influence LaTeX output. See further :doc:`latex`.
|
||||
.. deprecated:: 1.6
|
||||
This setting will be removed at Sphinx 1.7.
|
||||
|
||||
.. confval:: latex_use_latex_multicolumn
|
||||
|
||||
If ``False`` (default), the LaTeX writer uses for merged cells in grid
|
||||
tables Sphinx's own macros. They have the advantage to allow the same
|
||||
contents as in non-merged cells (inclusive of literal blocks, lists,
|
||||
blockquotes, ...). But they assume that the columns are separated by the
|
||||
standard vertical rule. Further, in case the :rst:dir:`tabularcolumns`
|
||||
directive was employed to inject more macros (using LaTeX's mark-up of the
|
||||
type ``>{..}``, ``<{..}``, ``@{..}``) the multicolumn cannot ignore these
|
||||
extra macros, contrarily to LaTeX's own ``\multicolumn``; but Sphinx's
|
||||
version does arrange for ignoring ``\columncolor`` like the standard
|
||||
``\multicolumn`` does. Setting to ``True`` means to use LaTeX's standard
|
||||
``\multicolumn`` macro.
|
||||
|
||||
.. versionadded:: 1.6
|
||||
|
||||
.. confval:: latex_elements
|
||||
|
||||
.. versionadded:: 0.5
|
||||
|
@ -237,32 +237,76 @@ following directive exists:
|
||||
in proportion to the observed shares in a first pass where the table cells
|
||||
are rendered at their natural "horizontal" widths.
|
||||
|
||||
By default, Sphinx uses a table layout with ``L`` for every column.
|
||||
|
||||
.. hint::
|
||||
|
||||
For columns which are known to be much narrower than the others it is
|
||||
recommended to use the lowercase specifiers. For more information, check
|
||||
the ``tabulary`` manual.
|
||||
By default, Sphinx uses a table layout with ``J`` for every column.
|
||||
|
||||
.. versionadded:: 0.3
|
||||
|
||||
.. warning::
|
||||
.. versionchanged:: 1.6
|
||||
Merged cells may now contain multiple paragraphs and are much better
|
||||
handled, thanks to custom Sphinx LaTeX macros. This novel situation
|
||||
motivated the switch to ``J`` specifier and not ``L`` by default.
|
||||
|
||||
Tables with more than 30 rows are rendered using ``longtable``, not
|
||||
``tabulary``, in order to allow pagebreaks.
|
||||
.. hint::
|
||||
|
||||
Tables that contain list-like elements such as object descriptions,
|
||||
blockquotes or any kind of lists cannot be set out of the box with
|
||||
``tabulary``. They are therefore set with the standard LaTeX ``tabular``
|
||||
environment if you don't give a ``tabularcolumns`` directive. If you do, the
|
||||
table will be set with ``tabulary``, but you must use the ``p{width}``
|
||||
construct for the columns that contain these elements.
|
||||
Sphinx actually uses ``T`` specifier having done ``\newcolumntype{T}{J}``.
|
||||
To revert to previous default, insert ``\newcolumntype{T}{L}`` in the
|
||||
LaTeX preamble (see :confval:`latex_elements`).
|
||||
|
||||
Literal blocks do not work with ``tabulary`` at all, so tables containing a
|
||||
literal block are always set with ``tabular``. Also, the verbatim
|
||||
environment used for literal blocks only works in ``p{width}`` columns, which
|
||||
means that by default, Sphinx generates such column specs for such tables.
|
||||
A frequent issue with tabulary is that columns with little contents are
|
||||
"squeezed". The minimal column width is a tabulary parameter called
|
||||
``\tymin``. You may set it globally in the LaTeX preamble via
|
||||
``\setlength{\tymin}{40pt}`` for example.
|
||||
|
||||
Else, use the :rst:dir:`tabularcolumns` directive with an explicit
|
||||
``p{40pt}`` (for example) for that column. You may use also ``l``
|
||||
specifier but this makes the task of setting column widths more difficult
|
||||
if some merged cell intersects that column.
|
||||
|
||||
.. warning::
|
||||
|
||||
Tables with more than 30 rows are rendered using ``longtable``, not
|
||||
``tabulary``, in order to allow pagebreaks. The ``L``, ``R``, ... specifiers
|
||||
do not work for these tables.
|
||||
|
||||
Tables that contain list-like elements such as object descriptions,
|
||||
blockquotes or any kind of lists cannot be set out of the box with
|
||||
``tabulary``. They are therefore set with the standard LaTeX ``tabular`` (or
|
||||
``longtable``) environment if you don't give a ``tabularcolumns`` directive.
|
||||
If you do, the table will be set with ``tabulary`` but you must use the
|
||||
``p{width}`` construct (or Sphinx's ``\X`` and ``\Y`` specifiers described
|
||||
below) for the columns containing these elements.
|
||||
|
||||
Literal blocks do not work with ``tabulary`` at all, so tables containing
|
||||
a literal block are always set with ``tabular``. The verbatim environment
|
||||
used for literal blocks only works in ``p{width}`` (and ``\X`` or ``\Y``)
|
||||
columns, hence Sphinx generates such column specs for tables containing
|
||||
literal blocks.
|
||||
|
||||
Since Sphinx 1.5, the ``\X{a}{b}`` specifier is used (there *is* a backslash
|
||||
in the specifier letter). It is like ``p{width}`` with the width set to a
|
||||
fraction ``a/b`` of the current line width. You can use it in the
|
||||
:rst:dir:`tabularcolumns` (it is not a problem if some LaTeX macro is also
|
||||
called ``\X``.)
|
||||
|
||||
It is *not* needed for ``b`` to be the total number of columns, nor for the
|
||||
sum of the fractions of the ``\X`` specifiers to add up to one. For example
|
||||
``|\X{2}{5}|\X{1}{5}|\X{1}{5}|`` is legitimate and the table will occupy
|
||||
80% of the line width, the first of its three columns having the same width
|
||||
as the sum of the next two.
|
||||
|
||||
This is used by the ``:widths:`` option of the :dudir:`table` directive.
|
||||
|
||||
Since Sphinx 1.6, there is also the ``\Y{f}`` specifier which admits a
|
||||
decimal argument, such has ``\Y{0.15}``: this would have the same effect as
|
||||
``\X{3}{20}``.
|
||||
|
||||
.. versionchanged:: 1.6
|
||||
|
||||
Merged cells from complex grid tables (either multi-row, multi-column, or
|
||||
both) now allow blockquotes, lists, literal blocks, ... as do regular cells.
|
||||
|
||||
Sphinx's merged cells interact well with ``p{width}``, ``\X{a}{b}``, ``Y{f}``
|
||||
and tabulary's columns.
|
||||
|
||||
.. rubric:: Footnotes
|
||||
|
||||
|
@ -297,6 +297,7 @@ def setup(app):
|
||||
app.add_config_value('latex_logo', None, None, string_classes)
|
||||
app.add_config_value('latex_appendices', [], None)
|
||||
app.add_config_value('latex_keep_old_macro_names', False, None)
|
||||
app.add_config_value('latex_use_latex_multicolumn', False, None)
|
||||
app.add_config_value('latex_toplevel_sectioning', None, None, [str])
|
||||
app.add_config_value('latex_domain_indices', True, None, [list])
|
||||
app.add_config_value('latex_show_urls', 'no', None)
|
||||
|
@ -27,7 +27,15 @@
|
||||
<%= sphinxsetup %>
|
||||
<%= geometry %>
|
||||
\usepackage{multirow}
|
||||
\let\originalmutirow\multirow\protected\def\multirow{%
|
||||
\sphinxdeprecationwarning{\multirow}{1.6}{1.7}
|
||||
{Sphinx does not use package multirow. Its loading will be removed at 1.7.}%
|
||||
\originalmultirow}%
|
||||
\usepackage{eqparbox}
|
||||
\let\originaleqparbox\eqparbox\protected\def\eqparbox{%
|
||||
\sphinxdeprecationwarning{\eqparbox}{1.6}{1.7}
|
||||
{Sphinx does not use package eqparbox. Its loading will be removed at 1.7.}%
|
||||
\originaleqparbox}%
|
||||
<%= usepackages %>
|
||||
<%= hyperref %>
|
||||
<%= contentsname %>
|
||||
|
@ -45,9 +45,30 @@
|
||||
******** ERROR !! PLEASE UPDATE titlesec.sty !!********^^J%
|
||||
******** THIS VERSION SWALLOWS SECTION NUMBERS.********}}}}{}
|
||||
\RequirePackage{tabulary}
|
||||
% use of \X to minimize possibility of conflict with one-character column types
|
||||
% tabulary has a bug with its re-definition of \multicolumn in its first pass
|
||||
% which is not \long. But now Sphinx does not use LaTeX's \multicolumn but its
|
||||
% own macro. Hence we don't even need to patch tabulary. See sphinxmulticell.sty
|
||||
% X or S (Sphinx) may have meanings if some table package is loaded hence
|
||||
% \X was chosen to avoid possibility of conflict
|
||||
\newcolumntype{\X}[2]{p{\dimexpr
|
||||
(\linewidth-\arrayrulewidth)*#1/#2-\tw@\tabcolsep-\arrayrulewidth\relax}}
|
||||
\newcolumntype{\Y}[1]{p{\dimexpr
|
||||
#1\dimexpr\linewidth-\arrayrulewidth\relax-\tw@\tabcolsep-\arrayrulewidth\relax}}
|
||||
% using here T (for Tabulary) feels less of a problem than the X could be
|
||||
\newcolumntype{T}{J}%
|
||||
% varwidth is crucial for our handling of general contents in merged cells
|
||||
\RequirePackage{varwidth}
|
||||
% but addition of a compatibility patch with hyperref is needed
|
||||
% (tested with varwidth v 0.92 Mar 2009)
|
||||
\AtBeginDocument {%
|
||||
\let\@@vwid@Hy@raisedlink\Hy@raisedlink
|
||||
\long\def\@vwid@Hy@raisedlink#1{\@vwid@wrap{\@@vwid@Hy@raisedlink{#1}}}%
|
||||
\edef\@vwid@setup{%
|
||||
\let\noexpand\Hy@raisedlink\noexpand\@vwid@Hy@raisedlink % HYPERREF !
|
||||
\unexpanded\expandafter{\@vwid@setup}}%
|
||||
}%
|
||||
% Homemade package to handle merged cells
|
||||
\RequirePackage{sphinxmulticell}
|
||||
\RequirePackage{makeidx}
|
||||
% For framing code-blocks and warning type notices, and shadowing topics
|
||||
\RequirePackage{framed}
|
||||
|
305
sphinx/texinputs/sphinxmulticell.sty
Normal file
305
sphinx/texinputs/sphinxmulticell.sty
Normal file
@ -0,0 +1,305 @@
|
||||
\NeedsTeXFormat{LaTeX2e}
|
||||
\ProvidesPackage{sphinxmulticell}%
|
||||
[2017/02/23 v1.6 better span rows and columns of a table (Sphinx team)]%
|
||||
\DeclareOption*{\PackageWarning{sphinxmulticell}{Option `\CurrentOption' is unknown}}%
|
||||
\ProcessOptions\relax
|
||||
%
|
||||
% --- MULTICOLUMN ---
|
||||
% standard LaTeX's \multicolumn
|
||||
% 1. does not allow verbatim contents,
|
||||
% 2. interacts very poorly with tabulary.
|
||||
%
|
||||
% It is needed to write own macros for Sphinx: to allow code-blocks in merged
|
||||
% cells rendered by tabular/longtable, and to allow multi-column cells with
|
||||
% paragraphs to be taken into account sanely by tabulary algorithm for column
|
||||
% widths.
|
||||
%
|
||||
% This requires quite a bit of hacking. First, in Sphinx, the multi-column
|
||||
% contents will *always* be wrapped in a varwidth environment. The issue
|
||||
% becomes to pass it the correct target width. We must trick tabulary into
|
||||
% believing the multicolumn is simply separate columns, else tabulary does not
|
||||
% incorporate the contents in its algorithm. But then we must clear the
|
||||
% vertical rules...
|
||||
%
|
||||
% configuration of tabulary
|
||||
\setlength{\tymin}{3\fontcharwd\font`0 }% minimal width of "squeezed" columns
|
||||
\setlength{\tymax}{10000pt}% allow enough room for paragraphs to "compete"
|
||||
% we need access to tabulary's final computed width. \@tempdima is too volatile
|
||||
% to hope it has kept tabulary's value when \sphinxcolwidth needs it.
|
||||
\newdimen\sphinx@TY@tablewidth
|
||||
\def\tabulary{%
|
||||
\def\TY@final{\sphinx@TY@tablewidth\@tempdima\tabular}%
|
||||
\let\endTY@final\endtabular
|
||||
\TY@tabular}%
|
||||
% next hack is needed only if user has set latex_use_latex_multicolumn to True:
|
||||
% it fixes tabulary's bug with \multicolumn defined "short" in first pass. (if
|
||||
% upstream tabulary adds a \long, our extra one causes no harm)
|
||||
\def\sphinx@tempa #1\def\multicolumn#2#3#4#5#6#7#8#9\sphinx@tempa
|
||||
{\def\TY@tab{#1\long\def\multicolumn####1####2####3{\multispan####1\relax}#9}}%
|
||||
\expandafter\sphinx@tempa\TY@tab\sphinx@tempa
|
||||
%
|
||||
% TN. 1: as \omit is never executed, Sphinx multicolumn does not need to worry
|
||||
% like standard multicolumn about |l| vs l|. On the other hand it assumes
|
||||
% columns are separated by a | ... (if not it will add extraneous
|
||||
% \arrayrulewidth space for each column separation in its estimate of available
|
||||
% width).
|
||||
%
|
||||
% TN. 1b: as Sphinx multicolumn uses neither \omit nor \span, it can not
|
||||
% (easily) get rid of extra macros from >{...} or <{...} between columns. At
|
||||
% least, it has been made compatible with colortbl's \columncolor.
|
||||
%
|
||||
% TN. 2: tabulary's second pass is handled like tabular/longtable's single
|
||||
% pass, with the difference that we hacked \TY@final to set in
|
||||
% \sphinx@TY@tablewidth the final target width as computed by tabulary. This is
|
||||
% needed only to handle columns with a "horizontal" specifier: "p" type columns
|
||||
% (inclusive of tabulary's LJRC) holds the target column width in the
|
||||
% \linewidth dimension.
|
||||
%
|
||||
% TN. 3: use of \begin{sphinxmulticolumn}...\end{sphinxmulticolumn} mark-up
|
||||
% would need some hacking around the fact that groups can not span across table
|
||||
% cells (the code does inserts & tokens, see TN1b). It was decided to keep it
|
||||
% simple with \sphinxstartmulticolumn...\sphinxstopmulticolumn.
|
||||
%
|
||||
% MEMO about nesting: if sphinxmulticolumn is encountered in a nested tabular
|
||||
% inside a tabulary it will think to be at top level in the tabulary. But
|
||||
% Sphinx generates no nested tables, and if some LaTeX macro uses internally a
|
||||
% tabular this will not have a \sphinxstartmulticolumn within it!
|
||||
%
|
||||
\def\sphinxstartmulticolumn{%
|
||||
\ifx\equation$% $ tabulary's first pass
|
||||
\expandafter\sphinx@TYI@start@multicolumn
|
||||
\else % either not tabulary or tabulary's second pass
|
||||
\expandafter\sphinx@start@multicolumn
|
||||
\fi
|
||||
}%
|
||||
\def\sphinxstopmulticolumn{%
|
||||
\ifx\equation$% $ tabulary's first pass
|
||||
\expandafter\sphinx@TYI@stop@multicolumn
|
||||
\else % either not tabulary or tabulary's second pass
|
||||
\ignorespaces
|
||||
\fi
|
||||
}%
|
||||
\def\sphinx@TYI@start@multicolumn#1{%
|
||||
% use \gdef always to avoid stack space build up
|
||||
\gdef\sphinx@tempa{#1}\begingroup\setbox\z@\hbox\bgroup
|
||||
}%
|
||||
\def\sphinx@TYI@stop@multicolumn{\egroup % varwidth was used with \tymax
|
||||
\xdef\sphinx@tempb{\the\dimexpr\wd\z@/\sphinx@tempa}% per column width
|
||||
\endgroup
|
||||
\expandafter\sphinx@TYI@multispan\expandafter{\sphinx@tempa}%
|
||||
}%
|
||||
\def\sphinx@TYI@multispan #1{%
|
||||
\kern\sphinx@tempb\ignorespaces % the per column occupied width
|
||||
\ifnum#1>\@ne % repeat, taking into account subtleties of TeX's & ...
|
||||
\expandafter\sphinx@TYI@multispan@next\expandafter{\the\numexpr#1-\@ne\expandafter}%
|
||||
\fi
|
||||
}%
|
||||
\def\sphinx@TYI@multispan@next{&\relax\sphinx@TYI@multispan}%
|
||||
%
|
||||
% Now the branch handling either the second pass of tabulary or the single pass
|
||||
% of tabular/longtable. This is the delicate part where we gather the
|
||||
% dimensions from the p columns either set-up by tabulary or by user p column
|
||||
% or Sphinx \X, \Y columns. The difficulty is that to get the said width, the
|
||||
% template must be inserted (other hacks would be horribly complicated except
|
||||
% if we rewrote crucial parts of LaTeX's \@array !) and we can not do
|
||||
% \omit\span like standard \multicolumn's easy approach. Thus we must cancel
|
||||
% the \vrule separators. Also, perhaps the column specifier is of the l, c, r
|
||||
% type, then we attempt an ad hoc rescue to give varwidth a reasonable target
|
||||
% width.
|
||||
\def\sphinx@start@multicolumn#1{%
|
||||
\gdef\sphinx@multiwidth{0pt}\gdef\sphinx@tempa{#1}\sphinx@multispan{#1}%
|
||||
}%
|
||||
\def\sphinx@multispan #1{%
|
||||
\ifnum#1=\@ne\expandafter\sphinx@multispan@end
|
||||
\else\expandafter\sphinx@multispan@next
|
||||
\fi {#1}%
|
||||
}%
|
||||
\def\sphinx@multispan@next #1{%
|
||||
% trick to recognize L, C, R, J or p, m, b type columns
|
||||
\ifdim\baselineskip>\z@
|
||||
\gdef\sphinx@tempb{\linewidth}%
|
||||
\else
|
||||
% if in an l, r, c type column, try and hope for the best
|
||||
\xdef\sphinx@tempb{\the\dimexpr(\ifx\TY@final\@undefined\linewidth\else
|
||||
\sphinx@TY@tablewidth\fi-\arrayrulewidth)/\sphinx@tempa
|
||||
-\tw@\tabcolsep-\arrayrulewidth\relax}%
|
||||
\fi
|
||||
\noindent\kern\sphinx@tempb\relax
|
||||
\xdef\sphinx@multiwidth
|
||||
{\the\dimexpr\sphinx@multiwidth+\sphinx@tempb+\tw@\tabcolsep+\arrayrulewidth}%
|
||||
% hack the \vline and the colortbl macros
|
||||
\aftergroup\sphinx@hack@vline\aftergroup\sphinx@hack@CT&\relax
|
||||
% repeat
|
||||
\expandafter\sphinx@multispan\expandafter{\the\numexpr#1-\@ne}%
|
||||
}%
|
||||
% packages like colortbl add group levels, we need to "climb back up" to be
|
||||
% able to hack the \vline and also the colortbl inserted tokens. This creates
|
||||
% empty space whether or not the columns were | separated:
|
||||
\def\sphinx@hack@vline{\ifnum\currentgrouptype=6\relax
|
||||
\kern\arrayrulewidth\arrayrulewidth\z@\else\aftergroup\sphinx@hack@vline\fi}%
|
||||
\def\sphinx@hack@CT{\ifnum\currentgrouptype=6\relax
|
||||
\let\CT@setup\sphinx@CT@setup\else\aftergroup\sphinx@hack@CT\fi}%
|
||||
% It turns out \CT@row@color is not expanded contrarily to \CT@column@color
|
||||
% during LaTeX+colortbl preamble preparation, hence it would be possible for
|
||||
% \sphinx@CT@setup to discard only the column color and choose to obey or not
|
||||
% row color and cell color. It would even be possible to propagate cell color
|
||||
% to row color for the duration of the Sphinx multicolumn... the (provisional?)
|
||||
% choice has been made to cancel the colortbl colours for the multicolumn
|
||||
% duration.
|
||||
\def\sphinx@CT@setup #1\endgroup{\endgroup}% hack to remove colour commands
|
||||
\def\sphinx@multispan@end#1{%
|
||||
% first, trace back our steps horizontally
|
||||
\noindent\kern-\dimexpr\sphinx@multiwidth\relax
|
||||
% and now we set the final computed width for the varwidth environment
|
||||
\ifdim\baselineskip>\z@
|
||||
\xdef\sphinx@multiwidth{\the\dimexpr\sphinx@multiwidth+\linewidth}%
|
||||
\else
|
||||
\xdef\sphinx@multiwidth{\the\dimexpr\sphinx@multiwidth+
|
||||
(\ifx\TY@final\@undefined\linewidth\else
|
||||
\sphinx@TY@tablewidth\fi-\arrayrulewidth)/\sphinx@tempa
|
||||
-\tw@\tabcolsep-\arrayrulewidth\relax}%
|
||||
\fi
|
||||
% we need to remove colour set-up also for last cell of the multi-column
|
||||
\aftergroup\sphinx@hack@CT
|
||||
}%
|
||||
\newcommand*\sphinxcolwidth[2]{%
|
||||
% this dimension will always be used for varwidth, and serves as maximum
|
||||
% width when cells are merged either via multirow or multicolumn or both,
|
||||
% as always their contents is wrapped in varwidth environment.
|
||||
\ifnum#1>\@ne % multi-column (and possibly also multi-row)
|
||||
% we wrote our own multicolumn code especially to handle that (and allow
|
||||
% verbatim contents)
|
||||
\ifx\equation$%$
|
||||
\tymax % first pass of tabulary (cf MEMO above regarding nesting)
|
||||
\else % the \@gobble thing is for compatibility with standard \multicolumn
|
||||
\sphinx@multiwidth\@gobble{#1/#2}%
|
||||
\fi
|
||||
\else % single column multirow
|
||||
\ifx\TY@final\@undefined % not a tabulary.
|
||||
\ifdim\baselineskip>\z@
|
||||
% in a p{..} type column, \linewidth is the target box width
|
||||
\linewidth
|
||||
\else
|
||||
% l, c, r columns. Do our best.
|
||||
\dimexpr(\linewidth-\arrayrulewidth)/#2-
|
||||
\tw@\tabcolsep-\arrayrulewidth\relax
|
||||
\fi
|
||||
\else % in tabulary
|
||||
\ifx\equation$%$% first pass
|
||||
\tymax % it is set to a big value so that paragraphs can express themselves
|
||||
\else
|
||||
% second pass.
|
||||
\ifdim\baselineskip>\z@
|
||||
\linewidth % in a L, R, C, J column or a p, \X, \Y ...
|
||||
\else
|
||||
% we have hacked \TY@final to put in \sphinx@TY@tablewidth the table width
|
||||
\dimexpr(\sphinx@TY@tablewidth-\arrayrulewidth)/#2-
|
||||
\tw@\tabcolsep-\arrayrulewidth\relax
|
||||
\fi
|
||||
\fi
|
||||
\fi
|
||||
\fi
|
||||
}%
|
||||
% fallback default in case user has set latex_use_latex_multicolumn to True:
|
||||
% \sphinxcolwidth will use this only inside LaTeX's standard \multicolumn
|
||||
\def\sphinx@multiwidth #1#2{\dimexpr % #1 to gobble the \@gobble (!)
|
||||
(\ifx\TY@final\@undefined\linewidth\else\sphinx@TY@tablewidth\fi
|
||||
-\arrayrulewidth)*#2-\tw@\tabcolsep-\arrayrulewidth\relax}%
|
||||
%
|
||||
% --- MULTIROW ---
|
||||
% standard \multirow
|
||||
% 1. does not allow verbatim contents,
|
||||
% 2. does not allow blank lines in its argument,
|
||||
% 3. requires manual intervention if the contents is too long to fit
|
||||
% in the asked-for number of rows.
|
||||
% The Sphinx solution consists in always having contents wrapped
|
||||
% in a varwidth environment so that it makes sense to estimate how many
|
||||
% lines it will occupy, and then ensure by insertion of suitable struts
|
||||
% that the table rows have the needed height. The needed mark-up is done
|
||||
% by LaTeX writer, which has its own id for the merged cells.
|
||||
%
|
||||
% In passing we obtain baseline alignements across rows (only if
|
||||
% \arraylinestretch is 1, as LaTeX's does not obey \arraylinestretch in "p"
|
||||
% multi-line contents, only first and last line...)
|
||||
%
|
||||
% TODO: examine the situation with \arraylinestretch > 1. The \extrarowheight
|
||||
% is hopeless for multirow anyhow, it makes baseline alignment strictly
|
||||
% impossible.
|
||||
\newcommand\sphinxmultirow[2]{\begingroup
|
||||
% #1 = nb of spanned rows, #2 = Sphinx id of "cell", #3 = contents
|
||||
% but let's fetch #3 in a way allowing verbatim contents !
|
||||
\def\sphinx@nbofrows{#1}\def\sphinx@cellid{#2}%
|
||||
\afterassignment\sphinx@multirow\let\next=
|
||||
}%
|
||||
\def\sphinx@multirow {%
|
||||
\setbox\z@\hbox\bgroup\aftergroup\sphinx@@multirow\strut
|
||||
}%
|
||||
\def\sphinx@@multirow {%
|
||||
% The contents, which is a varwidth environment, has been captured in
|
||||
% \box0 (a \hbox).
|
||||
% We have with \sphinx@cellid an assigned unique id. The goal is to give
|
||||
% about the same height to all the involved rows.
|
||||
% For this Sphinx will insert a \sphinxtablestrut{cell_id} mark-up
|
||||
% in LaTeX file and the expansion of the latter will do the suitable thing.
|
||||
\dimen@\dp\z@
|
||||
\dimen\tw@\ht\@arstrutbox
|
||||
\advance\dimen@\dimen\tw@
|
||||
\advance\dimen\tw@\dp\@arstrutbox
|
||||
\count@=\dimen@ % type conversion dim -> int
|
||||
\count\tw@=\dimen\tw@
|
||||
\divide\count@\count\tw@ % TeX division truncates
|
||||
\advance\dimen@-\count@\dimen\tw@
|
||||
% 1300sp is about 0.02pt. For comparison a rule default width is 0.4pt.
|
||||
% (note that if \count@ holds 0, surely \dimen@>1300sp)
|
||||
\ifdim\dimen@>1300sp \advance\count@\@ne \fi
|
||||
% now \count@ holds the count L of needed "lines"
|
||||
% and \sphinx@nbofrows holds the number N of rows
|
||||
% we have L >= 1 and N >= 1
|
||||
% if L is a multiple of N, ... clear what to do !
|
||||
% else write L = qN + r, 1 <= r < N and we will
|
||||
% arrange for each row to have enough space for:
|
||||
% q+1 "lines" in each of the first r rows
|
||||
% q "lines" in each of the (N-r) bottom rows
|
||||
% for a total of (q+1) * r + q * (N-r) = q * N + r = L
|
||||
% It is possible that q == 0.
|
||||
\count\tw@\count@
|
||||
% the TeX division truncates
|
||||
\divide\count\tw@\sphinx@nbofrows\relax
|
||||
\count4\count\tw@ % q
|
||||
\multiply\count\tw@\sphinx@nbofrows\relax
|
||||
\advance\count@-\count\tw@ % r
|
||||
\expandafter\xdef\csname sphinx@tablestrut_\sphinx@cellid\endcsname
|
||||
{\noexpand\sphinx@tablestrut{\the\count4}{\the\count@}{\sphinx@cellid}}%
|
||||
\dp\z@\z@
|
||||
% this will use the real height if it is >\ht\@arstrutbox
|
||||
\sphinxtablestrut{\sphinx@cellid}\box\z@
|
||||
\endgroup % group was opened in \sphinxmultirow
|
||||
}%
|
||||
\newcommand*\sphinxtablestrut[1]{%
|
||||
% #1 is a "cell_id", i.e. the id of a merged group of table cells
|
||||
\csname sphinx@tablestrut_#1\endcsname
|
||||
}%
|
||||
% LaTeX typesets the table row by row, hence each execution can do
|
||||
% an update for the next row.
|
||||
\newcommand*\sphinx@tablestrut[3]{\begingroup
|
||||
% #1 = q, #2 = (initially) r, #3 = cell_id, q+1 lines in first r rows
|
||||
% if #2 = 0, create space for max(q,1) table lines
|
||||
% if #2 > 0, create space for q+1 lines and decrement #2
|
||||
\leavevmode
|
||||
\count@#1\relax
|
||||
\ifnum#2=\z@
|
||||
\ifnum\count@=\z@\count@\@ne\fi
|
||||
\else
|
||||
% next row will be with a #2 decremented by one
|
||||
\expandafter\xdef\csname sphinx@tablestrut_#3\endcsname
|
||||
{\noexpand\sphinx@tablestrut{#1}{\the\numexpr#2-\@ne}{#3}}%
|
||||
\advance\count@\@ne
|
||||
\fi
|
||||
\vrule\@height\ht\@arstrutbox
|
||||
\@depth\dimexpr\count@\ht\@arstrutbox+\count@\dp\@arstrutbox-\ht\@arstrutbox\relax
|
||||
\@width\z@
|
||||
\endgroup
|
||||
}%
|
||||
\endinput
|
||||
%%
|
||||
%% End of file `sphinxmulticell.sty'.
|
@ -378,7 +378,7 @@ class Table(object):
|
||||
|
||||
This is what LaTeX calls the 'preamble argument' of the used table environment.
|
||||
|
||||
.. note:: the ``\\X`` column type specifier is defined in ``sphinx.sty``.
|
||||
.. note:: the ``\\X`` and ``T`` column type specifiers are defined in ``sphinx.sty``.
|
||||
"""
|
||||
if self.colspec:
|
||||
return self.colspec
|
||||
@ -389,7 +389,8 @@ class Table(object):
|
||||
elif self.has_problematic:
|
||||
return '{|*{%d}{\\X{1}{%d}|}}\n' % (self.colcount, self.colcount)
|
||||
elif self.get_table_type() == 'tabulary':
|
||||
return '{|' + ('L|' * self.colcount) + '}\n'
|
||||
# sphinx.sty sets T to be J by default.
|
||||
return '{|' + ('T|' * self.colcount) + '}\n'
|
||||
else:
|
||||
return '{|' + ('l|' * self.colcount) + '}\n'
|
||||
|
||||
@ -506,7 +507,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
|
||||
self.in_caption = 0
|
||||
self.in_container_literal_block = 0
|
||||
self.in_term = 0
|
||||
self.in_merged_cell = 0
|
||||
self.needs_linetrimming = 0
|
||||
self.in_minipage = 0
|
||||
self.first_document = 1
|
||||
self.this_is_the_title = 1
|
||||
@ -1382,10 +1383,15 @@ class LaTeXTranslator(nodes.NodeVisitor):
|
||||
break
|
||||
else: # a bottom of multirow cell
|
||||
self.table.col += cell.width
|
||||
if cell.col != 0:
|
||||
if cell.col:
|
||||
self.body.append('&')
|
||||
if cell.width > 1: # use \multicolumn for wide multirow cell
|
||||
self.body.append('\\multicolumn{%d}{|l|}{}\\relax ' % cell.width)
|
||||
if cell.width == 1:
|
||||
# insert suitable strut for equalizing row heights in given multirow
|
||||
self.body.append('\\sphinxtablestrut{%d}' % cell.cell_id)
|
||||
else: # use \multicolumn for wide multirow cell
|
||||
self.body.append('\\multicolumn{%d}{|l|}'
|
||||
'{\\sphinxtablestrut{%d}}' %
|
||||
(cell.width, cell.cell_id))
|
||||
|
||||
def depart_row(self, node):
|
||||
# type: (nodes.Node) -> None
|
||||
@ -1413,45 +1419,48 @@ class LaTeXTranslator(nodes.NodeVisitor):
|
||||
cell = self.table.cell()
|
||||
context = ''
|
||||
if cell.width > 1:
|
||||
self.body.append('\\multicolumn{%d}' % cell.width)
|
||||
if self.table.col == 0:
|
||||
self.body.append('{|l|}{\\relax ')
|
||||
if self.builder.config.latex_use_latex_multicolumn:
|
||||
if self.table.col == 0:
|
||||
self.body.append('\\multicolumn{%d}{|l|}{%%\n' % cell.width)
|
||||
else:
|
||||
self.body.append('\\multicolumn{%d}{l|}{%%\n' % cell.width)
|
||||
context = '}%\n'
|
||||
else:
|
||||
self.body.append('{l|}{\\relax ')
|
||||
context += '\\unskip}\\relax '
|
||||
self.body.append('\\sphinxstartmulticolumn{%d}%%\n' % cell.width)
|
||||
context = '\\sphinxstopmulticolumn\n'
|
||||
if cell.height > 1:
|
||||
self.body.append('\\multirow{%d}{*}{\\relax ' % cell.height)
|
||||
context += '\\unskip}\\relax '
|
||||
if (('morecols' in node or 'morerows' in node) and
|
||||
(len(node) > 2 or len(node.astext().split('\n')) > 2)):
|
||||
self.in_merged_cell = 1
|
||||
self.literal_whitespace += 1
|
||||
self.body.append('\\eqparbox{%d}{\\vspace{.5\\baselineskip}\n' % id(node))
|
||||
self.pushbody([])
|
||||
context += '}'
|
||||
# \sphinxmultirow 2nd arg "cell_id" will serve as id for LaTeX macros as well
|
||||
self.body.append('\\sphinxmultirow{%d}{%d}{%%\n' % (cell.height, cell.cell_id))
|
||||
context = '}%\n' + context
|
||||
if cell.width > 1 or cell.height > 1:
|
||||
self.body.append('\\begin{varwidth}[t]{\\sphinxcolwidth{%d}{%d}}\n'
|
||||
% (cell.width, self.table.colcount))
|
||||
context = ('\\par\n\\vskip-\\baselineskip\\strut\\end{varwidth}%\n') + context
|
||||
self.needs_linetrimming = 1
|
||||
if len(node) > 2 and len(node.astext().split('\n')) > 2:
|
||||
self.needs_linetrimming = 1
|
||||
if isinstance(node.parent.parent, nodes.thead):
|
||||
if len(node) == 1 and isinstance(node[0], nodes.paragraph) and node.astext() == '':
|
||||
pass
|
||||
else:
|
||||
self.body.append('\\sphinxstylethead{\\relax ')
|
||||
context += '\\unskip}\\relax '
|
||||
if len(node.traverse(nodes.paragraph)) >= 2:
|
||||
self.table.has_problematic = True
|
||||
context = '\\unskip}\\relax ' + context
|
||||
if self.needs_linetrimming:
|
||||
self.pushbody([])
|
||||
self.context.append(context)
|
||||
|
||||
def depart_entry(self, node):
|
||||
# type: (nodes.Node) -> None
|
||||
if self.in_merged_cell:
|
||||
self.in_merged_cell = 0
|
||||
self.literal_whitespace -= 1
|
||||
if self.needs_linetrimming:
|
||||
self.needs_linetrimming = 0
|
||||
body = self.popbody()
|
||||
|
||||
# Remove empty lines from top of merged cell
|
||||
while body and body[0] == "\n":
|
||||
body.pop(0)
|
||||
for line in body:
|
||||
line = re.sub(u'(?<!~\\\\\\\\)\n', u'~\\\\\\\\\n', line) # escape return code
|
||||
self.body.append(line)
|
||||
self.body.append(self.context.pop()) # header
|
||||
self.body.extend(body)
|
||||
|
||||
self.body.append(self.context.pop())
|
||||
|
||||
cell = self.table.cell()
|
||||
self.table.col += cell.width
|
||||
@ -1464,8 +1473,14 @@ class LaTeXTranslator(nodes.NodeVisitor):
|
||||
else: # a bottom part of multirow cell
|
||||
self.table.col += nextcell.width
|
||||
self.body.append('&')
|
||||
if nextcell.width > 1: # use \multicolumn for wide multirow cell
|
||||
self.body.append('\\multicolumn{%d}{l|}{}\\relax ' % nextcell.width)
|
||||
if nextcell.width == 1:
|
||||
# insert suitable strut for equalizing row heights in multirow
|
||||
self.body.append('\\sphinxtablestrut{%d}' % nextcell.cell_id)
|
||||
else:
|
||||
# use \multicolumn for wide multirow cell
|
||||
self.body.append('\\multicolumn{%d}{l|}'
|
||||
'{\\sphinxtablestrut{%d}}' %
|
||||
(nextcell.width, nextcell.cell_id))
|
||||
|
||||
def visit_acks(self, node):
|
||||
# type: (nodes.Node) -> None
|
||||
|
@ -846,7 +846,7 @@ def test_latex_table_tabulars(app, status, warning):
|
||||
# simple_table
|
||||
table = tables['simple table']
|
||||
assert ('\\begin{savenotes}\n\\centering\n'
|
||||
'\\begin{tabulary}{\\linewidth}{|L|L|}' in table)
|
||||
'\\begin{tabulary}{\\linewidth}{|T|T|}' in table)
|
||||
assert ('\\hline\n'
|
||||
'\\sphinxstylethead{\\relax \nheader1\n\\unskip}\\relax &'
|
||||
'\\sphinxstylethead{\\relax \nheader2\n\\unskip}\\relax' in table)
|
||||
@ -864,7 +864,7 @@ def test_latex_table_tabulars(app, status, warning):
|
||||
# table having :align: option (tabulary)
|
||||
table = tables['table having :align: option (tabulary)']
|
||||
assert ('\\begin{savenotes}\n\\raggedleft\n'
|
||||
'\\begin{tabulary}{\\linewidth}{|L|L|}\n' in table)
|
||||
'\\begin{tabulary}{\\linewidth}{|T|T|}\n' in table)
|
||||
assert ('\\hline\n\\end{tabulary}\n\\par\n\\end{savenotes}' in table)
|
||||
|
||||
# table having :align: option (tabular)
|
||||
@ -882,7 +882,7 @@ def test_latex_table_tabulars(app, status, warning):
|
||||
assert ('\\begin{savenotes}\n\\centering\n'
|
||||
'\\begin{threeparttable}\n\\capstart\\caption{caption for table}'
|
||||
'\\label{\\detokenize{tabular:id1}}' in table)
|
||||
assert ('\\begin{tabulary}{\\linewidth}{|L|L|}' in table)
|
||||
assert ('\\begin{tabulary}{\\linewidth}{|T|T|}' in table)
|
||||
assert ('\\hline\n\\end{tabulary}\n\\end{threeparttable}'
|
||||
'\n\\par\n\\end{savenotes}' in table)
|
||||
|
||||
@ -977,33 +977,46 @@ def test_latex_table_complex_tables(app, status, warning):
|
||||
|
||||
# grid table
|
||||
table = tables['grid table']
|
||||
assert ('\\begin{tabulary}{\\linewidth}{|L|L|L|}' in table)
|
||||
assert ('\\begin{tabulary}{\\linewidth}{|T|T|T|}' in table)
|
||||
assert ('\\hline\n'
|
||||
'\\sphinxstylethead{\\relax \nheader1\n\\unskip}\\relax &'
|
||||
'\\sphinxstylethead{\\relax \nheader2\n\\unskip}\\relax &'
|
||||
'\\sphinxstylethead{\\relax \nheader3\n\\unskip}\\relax \\\\' in table)
|
||||
assert ('\\hline\ncell1-1\n&\\multirow{2}{*}{\\relax \ncell1-2\n\\unskip}\\relax &\n'
|
||||
'cell1-3\n\\\\' in table)
|
||||
assert ('\\cline{1-1}\\cline{3-3}\\multirow{2}{*}{\\relax \ncell2-1\n\\unskip}\\relax &&\n'
|
||||
'cell2-3\n\\\\' in table)
|
||||
assert ('\\cline{2-3}&\\multicolumn{2}{l|}{\\relax \\multirow{2}{*}{\\relax \n'
|
||||
'cell3-2\n\\unskip}\\relax \\unskip}\\relax \\\\' in table)
|
||||
assert ('\\cline{1-1}\ncell4-1\n&\\multicolumn{2}{l|}{}\\relax \\\\' in table)
|
||||
assert ('\\hline\\multicolumn{3}{|l|}{\\relax \ncell5-1\n\\unskip}\\relax \\\\\n'
|
||||
'\\hline\n\\end{tabulary}' in table)
|
||||
assert ('\\hline\ncell1-1\n&\\sphinxmultirow{2}{5}{%\n\\begin{varwidth}[t]'
|
||||
'{\\sphinxcolwidth{1}{3}}\n'
|
||||
'cell1-2\n\\par\n' in table)
|
||||
assert ('\\cline{1-1}\\cline{3-3}\\sphinxmultirow{2}{7}{%\n' in table)
|
||||
assert ('&\\sphinxtablestrut{5}&\ncell2-3\n\\\\\n'
|
||||
'\\cline{2-3}\\sphinxtablestrut{7}&\\sphinxstartmulticolumn{2}%\n'
|
||||
'\\sphinxmultirow{2}{9}{%\n\\begin{varwidth}' in table)
|
||||
assert ('\\cline{1-1}\ncell4-1\n&\\multicolumn{2}{l|}'
|
||||
'{\\sphinxtablestrut{9}}\\\\' in table)
|
||||
assert ('\\hline\\sphinxstartmulticolumn{3}%\n'
|
||||
in table)
|
||||
|
||||
# complex spanning cell
|
||||
table = tables['complex spanning cell']
|
||||
assert ('\\begin{tabulary}{\\linewidth}{|L|L|L|L|L|}' in table)
|
||||
assert ('\\hline\n'
|
||||
'\\multirow{3}{*}{\\relax \ncell1-1\n\\unskip}\\relax &'
|
||||
'\\multirow{3}{*}{\\relax \ncell1-2\n\\unskip}\\relax &'
|
||||
'\ncell1-3\n&'
|
||||
'\\multirow{3}{*}{\\relax \ncell1-4\n\\unskip}\\relax &'
|
||||
'\\multirow{2}{*}{\\relax \ncell1-5\n\\unskip}\\relax \\\\\n'
|
||||
assert ('\\begin{tabulary}{\\linewidth}{|T|T|T|T|T|}' in table)
|
||||
assert ('\\sphinxmultirow{3}{1}{%\n'
|
||||
'\\begin{varwidth}[t]{\\sphinxcolwidth{1}{5}}\n'
|
||||
'cell1-1\n\\par\n\\vskip-\\baselineskip\\strut\\end{varwidth}%\n'
|
||||
'}%\n'
|
||||
'&\\sphinxmultirow{3}{2}{%\n'
|
||||
'\\begin{varwidth}[t]{\\sphinxcolwidth{1}{5}}\n'
|
||||
'cell1-2\n\\par\n\\vskip-\\baselineskip\\strut\\end{varwidth}%\n'
|
||||
'}%\n&\ncell1-3\n&\\sphinxmultirow{3}{4}{%\n'
|
||||
'\\begin{varwidth}[t]{\\sphinxcolwidth{1}{5}}\n'
|
||||
'cell1-4\n\\par\n\\vskip-\\baselineskip\\strut\\end{varwidth}%\n'
|
||||
'}%\n'
|
||||
'&\\sphinxmultirow{2}{5}{%\n'
|
||||
'\\begin{varwidth}[t]{\\sphinxcolwidth{1}{5}}\n'
|
||||
'cell1-5\n'
|
||||
in table)
|
||||
assert ('\\cline{3-3}&&'
|
||||
'\\multirow{2}{*}{\\relax \ncell2-3\n\\unskip}\\relax &&\\\\\n'
|
||||
assert ('\\cline{3-3}\\sphinxtablestrut{1}&\\sphinxtablestrut{2}&'
|
||||
'\\sphinxmultirow{2}{6}{%\n'
|
||||
in table)
|
||||
assert ('&\\sphinxtablestrut{4}&\\sphinxtablestrut{5}\\\\\n'
|
||||
'\\cline{5-5}\\sphinxtablestrut{1}&\\sphinxtablestrut{2}&'
|
||||
'\\sphinxtablestrut{6}&\\sphinxtablestrut{4}&\ncell3-5\n'
|
||||
'\\\\\n\\hline\n\\end{tabulary}\n'
|
||||
in table)
|
||||
assert ('\\cline{5-5}&&&&\ncell3-5\n\\\\\n' in table)
|
||||
assert ('\\hline\n\\end{tabulary}' in table)
|
||||
|
Loading…
Reference in New Issue
Block a user