1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
%% TABLES (WITH SUPPORT FOR MERGED CELLS OF GENERAL CONTENTS)
%
% change this info string if making any custom modification
\ProvidesFile{sphinxlatextables.sty}[2021/01/27 tables]%

% Provides support for this output mark-up from Sphinx latex writer
% and table templates:
%
% - the tabulary and longtable environments from the eponymous packages
% - the varwidth environment
% - the >{} etc mark-up possible in tabularcolumns is from array package
%     which is loaded by longtable and tabulary
% - \X, \Y, T column types; others (L, C, R, J) are from tabulary package
% - \sphinxaftertopcaption
% - \sphinxatlongtableend
% - \sphinxatlongtablestart
% - \sphinxattableend
% - \sphinxattablestart
% - \sphinxcapstartof
% - \sphinxcolwidth
% - \sphinxlongtablecapskipadjust
% - \sphinxmultirow
% - \sphinxstartmulticolumn
% - \sphinxstopmulticolumn
% - \sphinxtablestrut
% - \sphinxthecaptionisattop
% - \sphinxthelongtablecaptionisattop
%
% Executes \RequirePackage for:
%
% - tabulary
% - longtable
% - varwidth
%
% Extends tabulary and longtable via patches and custom macros to support
% merged cells possibly containing code-blocks in complex tables

\RequirePackage{tabulary}
% 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
% sphinxpackagemulticell.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}%
% For tables allowing pagebreaks
\RequirePackage{longtable}
% User interface to set-up whitespace before and after tables:
\newcommand*\sphinxtablepre {0pt}%
\newcommand*\sphinxtablepost{\medskipamount}%
% Space from caption baseline to top of table or frame of literal-block
\newcommand*\sphinxbelowcaptionspace{.5\sphinxbaselineskip}%
% as one can not use \baselineskip from inside longtable (it is zero there)
% we need \sphinxbaselineskip, which defaults to \baselineskip
\def\sphinxbaselineskip{\baselineskip}%
% The following is to ensure that, whether tabular(y) or longtable:
% - if a caption is on top of table:
%   a) the space between its last baseline and the top rule of table is
%      exactly \sphinxbelowcaptionspace
%   b) the space from last baseline of previous text to first baseline of
%      caption is exactly \parskip+\baselineskip+ height of a strut.
%   c) the caption text will wrap at width \LTcapwidth (4in)
% - make sure this works also if "caption" package is loaded by user
%   (with its width or margin option taking place of \LTcapwidth role)
% TODO: obtain same for caption of literal block: a) & c) DONE, b) TO BE DONE
%
% To modify space below such top caption, adjust \sphinxbelowcaptionspace
% To add or remove space above such top caption, adjust \sphinxtablepre:
%   notice that \abovecaptionskip, \belowcaptionskip, \LTpre are **ignored**
% A. Table with longtable
\def\sphinxatlongtablestart
   {\par
    \vskip\parskip
    \vskip\dimexpr\sphinxtablepre\relax % adjust vertical position
    \vbox{}% get correct baseline from above
    \LTpre\z@skip\LTpost\z@skip % set to zero longtable's own skips
    \edef\sphinxbaselineskip{\dimexpr\the\dimexpr\baselineskip\relax\relax}%
   }%
% Compatibility with caption package
\def\sphinxthelongtablecaptionisattop{%
    \spx@ifcaptionpackage{\noalign{\vskip-\belowcaptionskip}}{}%
}%
% Achieves exactly \sphinxbelowcaptionspace below longtable caption
\def\sphinxlongtablecapskipadjust
   {\dimexpr-\dp\strutbox
            -\spx@ifcaptionpackage{\abovecaptionskip}{\sphinxbaselineskip}%
            +\sphinxbelowcaptionspace\relax}%
\def\sphinxatlongtableend{\@nobreakfalse % latex3/latex2e#173
    \prevdepth\z@\vskip\sphinxtablepost\relax}%
% B. Table with tabular or tabulary
\def\sphinxattablestart{\par\vskip\dimexpr\sphinxtablepre\relax}%
\let\sphinxattableend\sphinxatlongtableend
% This is used by tabular and tabulary templates
\newcommand*\sphinxcapstartof[1]{%
   \vskip\parskip
   \vbox{}% force baselineskip for good positioning by capstart of hyperanchor
   % hyperref puts the anchor 6pt above this baseline; in case of caption
   % this baseline will be \ht\strutbox above first baseline of caption
   \def\@captype{#1}%
   \capstart
% move back vertically, as tabular (or its caption) will compensate
   \vskip-\baselineskip\vskip-\parskip
}%
\def\sphinxthecaptionisattop{% locate it after \sphinxcapstartof
    \spx@ifcaptionpackage
      {\caption@setposition{t}%
       \vskip\baselineskip\vskip\parskip % undo those from \sphinxcapstartof
       \vskip-\belowcaptionskip          % anticipate caption package skip
       % caption package uses a \vbox, not a \vtop, so "single line" case
       % gives different result from "multi-line" without this:
       \nointerlineskip
      }%
      {}%
}%
\def\sphinxthecaptionisatbottom{% (not finalized; for template usage)
    \spx@ifcaptionpackage{\caption@setposition{b}}{}%
}%
% The aim of \sphinxcaption is to apply to tabular(y) the maximal width
% of caption as done by longtable
\def\sphinxtablecapwidth{\LTcapwidth}%
\newcommand\sphinxcaption{\@dblarg\spx@caption}%
\long\def\spx@caption[#1]#2{%
   \noindent\hb@xt@\linewidth{\hss
      \vtop{\@tempdima\dimexpr\sphinxtablecapwidth\relax
% don't exceed linewidth for the caption width
            \ifdim\@tempdima>\linewidth\hsize\linewidth\else\hsize\@tempdima\fi
% longtable ignores \abovecaptionskip/\belowcaptionskip, so do the same here
            \abovecaptionskip\sphinxabovecaptionskip % \z@skip
            \belowcaptionskip\sphinxbelowcaptionskip % \z@skip
            \caption[{#1}]%
               {\strut\ignorespaces#2\ifhmode\unskip\@finalstrut\strutbox\fi}%
           }\hss}%
   \par\prevdepth\dp\strutbox
}%
\def\sphinxabovecaptionskip{\z@skip}% Do not use! Flagged for removal
\def\sphinxbelowcaptionskip{\z@skip}% Do not use! Flagged for removal
% This wrapper of \abovecaptionskip is used in sphinxVerbatim for top
% caption, and with another value in sphinxVerbatimintable
% TODO: To unify space above caption of a code-block with the one above
%       caption of a table/longtable, \abovecaptionskip must not be used
%       This auxiliary will get renamed and receive a different meaning
%       in future.
\def\spx@abovecaptionskip{\abovecaptionskip}%
% Achieve \sphinxbelowcaptionspace below a caption located above a tabular
% or a tabulary
\newcommand\sphinxaftertopcaption
{%
   \spx@ifcaptionpackage
     {\par\prevdepth\dp\strutbox\nobreak\vskip-\abovecaptionskip}{\nobreak}%
   \vskip\dimexpr\sphinxbelowcaptionspace\relax
   \vskip-\baselineskip\vskip-\parskip
}%
% 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}}%
}%

%%%%%%%%%%%%%%%%%%%%%
% --- 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
    \sphinx@hack@vline\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. its * specifier means to typeset "horizontally" which is very
%    bad for paragraph content. 2016 version has = specifier but it
%    must be used with p type columns only, else results are bad,
% 4. it requires manual intervention if the contents is too long to fit
%    in the asked-for number of rows.
% 5. colour panels (either from \rowcolor or \columncolor) will hide
%    the bottom part of multirow text, hence manual tuning is needed
%    to put the multirow insertion at the _bottom_.
%
% 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.
%
% The colour issue is solved by clearing colour panels in all cells,
% whether or not the multirow is single-column or multi-column.
%
% In passing we obtain baseline alignements across rows (only if
% \arraystretch is 1, as LaTeX's does not obey \arraystretch in "p"
% multi-line contents, only first and last line...)
%
% TODO: examine the situation with \arraystretch > 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
  % we need this to avoid colour panels hiding bottom parts of multirow text
  \sphinx@hack@CT
}%

\endinput