1 | \NeedsTeXFormat{LaTeX2e} |
---|
2 | \ProvidesPackage{sphinxmulticell}% |
---|
3 | [2017/02/23 v1.6 better span rows and columns of a table (Sphinx team)]% |
---|
4 | \DeclareOption*{\PackageWarning{sphinxmulticell}{Option `\CurrentOption' is unknown}}% |
---|
5 | \ProcessOptions\relax |
---|
6 | % |
---|
7 | % --- MULTICOLUMN --- |
---|
8 | % standard LaTeX's \multicolumn |
---|
9 | % 1. does not allow verbatim contents, |
---|
10 | % 2. interacts very poorly with tabulary. |
---|
11 | % |
---|
12 | % It is needed to write own macros for Sphinx: to allow code-blocks in merged |
---|
13 | % cells rendered by tabular/longtable, and to allow multi-column cells with |
---|
14 | % paragraphs to be taken into account sanely by tabulary algorithm for column |
---|
15 | % widths. |
---|
16 | % |
---|
17 | % This requires quite a bit of hacking. First, in Sphinx, the multi-column |
---|
18 | % contents will *always* be wrapped in a varwidth environment. The issue |
---|
19 | % becomes to pass it the correct target width. We must trick tabulary into |
---|
20 | % believing the multicolumn is simply separate columns, else tabulary does not |
---|
21 | % incorporate the contents in its algorithm. But then we must clear the |
---|
22 | % vertical rules... |
---|
23 | % |
---|
24 | % configuration of tabulary |
---|
25 | \setlength{\tymin}{3\fontcharwd\font`0 }% minimal width of "squeezed" columns |
---|
26 | \setlength{\tymax}{10000pt}% allow enough room for paragraphs to "compete" |
---|
27 | % we need access to tabulary's final computed width. \@tempdima is too volatile |
---|
28 | % to hope it has kept tabulary's value when \sphinxcolwidth needs it. |
---|
29 | \newdimen\sphinx@TY@tablewidth |
---|
30 | \def\tabulary{% |
---|
31 | \def\TY@final{\sphinx@TY@tablewidth\@tempdima\tabular}% |
---|
32 | \let\endTY@final\endtabular |
---|
33 | \TY@tabular}% |
---|
34 | % next hack is needed only if user has set latex_use_latex_multicolumn to True: |
---|
35 | % it fixes tabulary's bug with \multicolumn defined "short" in first pass. (if |
---|
36 | % upstream tabulary adds a \long, our extra one causes no harm) |
---|
37 | \def\sphinx@tempa #1\def\multicolumn#2#3#4#5#6#7#8#9\sphinx@tempa |
---|
38 | {\def\TY@tab{#1\long\def\multicolumn####1####2####3{\multispan####1\relax}#9}}% |
---|
39 | \expandafter\sphinx@tempa\TY@tab\sphinx@tempa |
---|
40 | % |
---|
41 | % TN. 1: as \omit is never executed, Sphinx multicolumn does not need to worry |
---|
42 | % like standard multicolumn about |l| vs l|. On the other hand it assumes |
---|
43 | % columns are separated by a | ... (if not it will add extraneous |
---|
44 | % \arrayrulewidth space for each column separation in its estimate of available |
---|
45 | % width). |
---|
46 | % |
---|
47 | % TN. 1b: as Sphinx multicolumn uses neither \omit nor \span, it can not |
---|
48 | % (easily) get rid of extra macros from >{...} or <{...} between columns. At |
---|
49 | % least, it has been made compatible with colortbl's \columncolor. |
---|
50 | % |
---|
51 | % TN. 2: tabulary's second pass is handled like tabular/longtable's single |
---|
52 | % pass, with the difference that we hacked \TY@final to set in |
---|
53 | % \sphinx@TY@tablewidth the final target width as computed by tabulary. This is |
---|
54 | % needed only to handle columns with a "horizontal" specifier: "p" type columns |
---|
55 | % (inclusive of tabulary's LJRC) holds the target column width in the |
---|
56 | % \linewidth dimension. |
---|
57 | % |
---|
58 | % TN. 3: use of \begin{sphinxmulticolumn}...\end{sphinxmulticolumn} mark-up |
---|
59 | % would need some hacking around the fact that groups can not span across table |
---|
60 | % cells (the code does inserts & tokens, see TN1b). It was decided to keep it |
---|
61 | % simple with \sphinxstartmulticolumn...\sphinxstopmulticolumn. |
---|
62 | % |
---|
63 | % MEMO about nesting: if sphinxmulticolumn is encountered in a nested tabular |
---|
64 | % inside a tabulary it will think to be at top level in the tabulary. But |
---|
65 | % Sphinx generates no nested tables, and if some LaTeX macro uses internally a |
---|
66 | % tabular this will not have a \sphinxstartmulticolumn within it! |
---|
67 | % |
---|
68 | \def\sphinxstartmulticolumn{% |
---|
69 | \ifx\equation$% $ tabulary's first pass |
---|
70 | \expandafter\sphinx@TYI@start@multicolumn |
---|
71 | \else % either not tabulary or tabulary's second pass |
---|
72 | \expandafter\sphinx@start@multicolumn |
---|
73 | \fi |
---|
74 | }% |
---|
75 | \def\sphinxstopmulticolumn{% |
---|
76 | \ifx\equation$% $ tabulary's first pass |
---|
77 | \expandafter\sphinx@TYI@stop@multicolumn |
---|
78 | \else % either not tabulary or tabulary's second pass |
---|
79 | \ignorespaces |
---|
80 | \fi |
---|
81 | }% |
---|
82 | \def\sphinx@TYI@start@multicolumn#1{% |
---|
83 | % use \gdef always to avoid stack space build up |
---|
84 | \gdef\sphinx@tempa{#1}\begingroup\setbox\z@\hbox\bgroup |
---|
85 | }% |
---|
86 | \def\sphinx@TYI@stop@multicolumn{\egroup % varwidth was used with \tymax |
---|
87 | \xdef\sphinx@tempb{\the\dimexpr\wd\z@/\sphinx@tempa}% per column width |
---|
88 | \endgroup |
---|
89 | \expandafter\sphinx@TYI@multispan\expandafter{\sphinx@tempa}% |
---|
90 | }% |
---|
91 | \def\sphinx@TYI@multispan #1{% |
---|
92 | \kern\sphinx@tempb\ignorespaces % the per column occupied width |
---|
93 | \ifnum#1>\@ne % repeat, taking into account subtleties of TeX's & ... |
---|
94 | \expandafter\sphinx@TYI@multispan@next\expandafter{\the\numexpr#1-\@ne\expandafter}% |
---|
95 | \fi |
---|
96 | }% |
---|
97 | \def\sphinx@TYI@multispan@next{&\relax\sphinx@TYI@multispan}% |
---|
98 | % |
---|
99 | % Now the branch handling either the second pass of tabulary or the single pass |
---|
100 | % of tabular/longtable. This is the delicate part where we gather the |
---|
101 | % dimensions from the p columns either set-up by tabulary or by user p column |
---|
102 | % or Sphinx \X, \Y columns. The difficulty is that to get the said width, the |
---|
103 | % template must be inserted (other hacks would be horribly complicated except |
---|
104 | % if we rewrote crucial parts of LaTeX's \@array !) and we can not do |
---|
105 | % \omit\span like standard \multicolumn's easy approach. Thus we must cancel |
---|
106 | % the \vrule separators. Also, perhaps the column specifier is of the l, c, r |
---|
107 | % type, then we attempt an ad hoc rescue to give varwidth a reasonable target |
---|
108 | % width. |
---|
109 | \def\sphinx@start@multicolumn#1{% |
---|
110 | \gdef\sphinx@multiwidth{0pt}\gdef\sphinx@tempa{#1}\sphinx@multispan{#1}% |
---|
111 | }% |
---|
112 | \def\sphinx@multispan #1{% |
---|
113 | \ifnum#1=\@ne\expandafter\sphinx@multispan@end |
---|
114 | \else\expandafter\sphinx@multispan@next |
---|
115 | \fi {#1}% |
---|
116 | }% |
---|
117 | \def\sphinx@multispan@next #1{% |
---|
118 | % trick to recognize L, C, R, J or p, m, b type columns |
---|
119 | \ifdim\baselineskip>\z@ |
---|
120 | \gdef\sphinx@tempb{\linewidth}% |
---|
121 | \else |
---|
122 | % if in an l, r, c type column, try and hope for the best |
---|
123 | \xdef\sphinx@tempb{\the\dimexpr(\ifx\TY@final\@undefined\linewidth\else |
---|
124 | \sphinx@TY@tablewidth\fi-\arrayrulewidth)/\sphinx@tempa |
---|
125 | -\tw@\tabcolsep-\arrayrulewidth\relax}% |
---|
126 | \fi |
---|
127 | \noindent\kern\sphinx@tempb\relax |
---|
128 | \xdef\sphinx@multiwidth |
---|
129 | {\the\dimexpr\sphinx@multiwidth+\sphinx@tempb+\tw@\tabcolsep+\arrayrulewidth}% |
---|
130 | % hack the \vline and the colortbl macros |
---|
131 | \sphinx@hack@vline\sphinx@hack@CT&\relax |
---|
132 | % repeat |
---|
133 | \expandafter\sphinx@multispan\expandafter{\the\numexpr#1-\@ne}% |
---|
134 | }% |
---|
135 | % packages like colortbl add group levels, we need to "climb back up" to be |
---|
136 | % able to hack the \vline and also the colortbl inserted tokens. This creates |
---|
137 | % empty space whether or not the columns were | separated: |
---|
138 | \def\sphinx@hack@vline{\ifnum\currentgrouptype=6\relax |
---|
139 | \kern\arrayrulewidth\arrayrulewidth\z@\else\aftergroup\sphinx@hack@vline\fi}% |
---|
140 | \def\sphinx@hack@CT{\ifnum\currentgrouptype=6\relax |
---|
141 | \let\CT@setup\sphinx@CT@setup\else\aftergroup\sphinx@hack@CT\fi}% |
---|
142 | % It turns out \CT@row@color is not expanded contrarily to \CT@column@color |
---|
143 | % during LaTeX+colortbl preamble preparation, hence it would be possible for |
---|
144 | % \sphinx@CT@setup to discard only the column color and choose to obey or not |
---|
145 | % row color and cell color. It would even be possible to propagate cell color |
---|
146 | % to row color for the duration of the Sphinx multicolumn... the (provisional?) |
---|
147 | % choice has been made to cancel the colortbl colours for the multicolumn |
---|
148 | % duration. |
---|
149 | \def\sphinx@CT@setup #1\endgroup{\endgroup}% hack to remove colour commands |
---|
150 | \def\sphinx@multispan@end#1{% |
---|
151 | % first, trace back our steps horizontally |
---|
152 | \noindent\kern-\dimexpr\sphinx@multiwidth\relax |
---|
153 | % and now we set the final computed width for the varwidth environment |
---|
154 | \ifdim\baselineskip>\z@ |
---|
155 | \xdef\sphinx@multiwidth{\the\dimexpr\sphinx@multiwidth+\linewidth}% |
---|
156 | \else |
---|
157 | \xdef\sphinx@multiwidth{\the\dimexpr\sphinx@multiwidth+ |
---|
158 | (\ifx\TY@final\@undefined\linewidth\else |
---|
159 | \sphinx@TY@tablewidth\fi-\arrayrulewidth)/\sphinx@tempa |
---|
160 | -\tw@\tabcolsep-\arrayrulewidth\relax}% |
---|
161 | \fi |
---|
162 | % we need to remove colour set-up also for last cell of the multi-column |
---|
163 | \aftergroup\sphinx@hack@CT |
---|
164 | }% |
---|
165 | \newcommand*\sphinxcolwidth[2]{% |
---|
166 | % this dimension will always be used for varwidth, and serves as maximum |
---|
167 | % width when cells are merged either via multirow or multicolumn or both, |
---|
168 | % as always their contents is wrapped in varwidth environment. |
---|
169 | \ifnum#1>\@ne % multi-column (and possibly also multi-row) |
---|
170 | % we wrote our own multicolumn code especially to handle that (and allow |
---|
171 | % verbatim contents) |
---|
172 | \ifx\equation$%$ |
---|
173 | \tymax % first pass of tabulary (cf MEMO above regarding nesting) |
---|
174 | \else % the \@gobble thing is for compatibility with standard \multicolumn |
---|
175 | \sphinx@multiwidth\@gobble{#1/#2}% |
---|
176 | \fi |
---|
177 | \else % single column multirow |
---|
178 | \ifx\TY@final\@undefined % not a tabulary. |
---|
179 | \ifdim\baselineskip>\z@ |
---|
180 | % in a p{..} type column, \linewidth is the target box width |
---|
181 | \linewidth |
---|
182 | \else |
---|
183 | % l, c, r columns. Do our best. |
---|
184 | \dimexpr(\linewidth-\arrayrulewidth)/#2- |
---|
185 | \tw@\tabcolsep-\arrayrulewidth\relax |
---|
186 | \fi |
---|
187 | \else % in tabulary |
---|
188 | \ifx\equation$%$% first pass |
---|
189 | \tymax % it is set to a big value so that paragraphs can express themselves |
---|
190 | \else |
---|
191 | % second pass. |
---|
192 | \ifdim\baselineskip>\z@ |
---|
193 | \linewidth % in a L, R, C, J column or a p, \X, \Y ... |
---|
194 | \else |
---|
195 | % we have hacked \TY@final to put in \sphinx@TY@tablewidth the table width |
---|
196 | \dimexpr(\sphinx@TY@tablewidth-\arrayrulewidth)/#2- |
---|
197 | \tw@\tabcolsep-\arrayrulewidth\relax |
---|
198 | \fi |
---|
199 | \fi |
---|
200 | \fi |
---|
201 | \fi |
---|
202 | }% |
---|
203 | % fallback default in case user has set latex_use_latex_multicolumn to True: |
---|
204 | % \sphinxcolwidth will use this only inside LaTeX's standard \multicolumn |
---|
205 | \def\sphinx@multiwidth #1#2{\dimexpr % #1 to gobble the \@gobble (!) |
---|
206 | (\ifx\TY@final\@undefined\linewidth\else\sphinx@TY@tablewidth\fi |
---|
207 | -\arrayrulewidth)*#2-\tw@\tabcolsep-\arrayrulewidth\relax}% |
---|
208 | % |
---|
209 | % --- MULTIROW --- |
---|
210 | % standard \multirow |
---|
211 | % 1. does not allow verbatim contents, |
---|
212 | % 2. does not allow blank lines in its argument, |
---|
213 | % 3. its * specifier means to typeset "horizontally" which is very |
---|
214 | % bad for paragraph content. 2016 version has = specifier but it |
---|
215 | % must be used with p type columns only, else results are bad, |
---|
216 | % 4. it requires manual intervention if the contents is too long to fit |
---|
217 | % in the asked-for number of rows. |
---|
218 | % 5. colour panels (either from \rowcolor or \columncolor) will hide |
---|
219 | % the bottom part of multirow text, hence manual tuning is needed |
---|
220 | % to put the multirow insertion at the _bottom_. |
---|
221 | % |
---|
222 | % The Sphinx solution consists in always having contents wrapped |
---|
223 | % in a varwidth environment so that it makes sense to estimate how many |
---|
224 | % lines it will occupy, and then ensure by insertion of suitable struts |
---|
225 | % that the table rows have the needed height. The needed mark-up is done |
---|
226 | % by LaTeX writer, which has its own id for the merged cells. |
---|
227 | % |
---|
228 | % The colour issue is solved by clearing colour panels in all cells, |
---|
229 | % whether or not the multirow is single-column or multi-column. |
---|
230 | % |
---|
231 | % In passing we obtain baseline alignements across rows (only if |
---|
232 | % \arraylinestretch is 1, as LaTeX's does not obey \arraylinestretch in "p" |
---|
233 | % multi-line contents, only first and last line...) |
---|
234 | % |
---|
235 | % TODO: examine the situation with \arraylinestretch > 1. The \extrarowheight |
---|
236 | % is hopeless for multirow anyhow, it makes baseline alignment strictly |
---|
237 | % impossible. |
---|
238 | \newcommand\sphinxmultirow[2]{\begingroup |
---|
239 | % #1 = nb of spanned rows, #2 = Sphinx id of "cell", #3 = contents |
---|
240 | % but let's fetch #3 in a way allowing verbatim contents ! |
---|
241 | \def\sphinx@nbofrows{#1}\def\sphinx@cellid{#2}% |
---|
242 | \afterassignment\sphinx@multirow\let\next= |
---|
243 | }% |
---|
244 | \def\sphinx@multirow {% |
---|
245 | \setbox\z@\hbox\bgroup\aftergroup\sphinx@@multirow\strut |
---|
246 | }% |
---|
247 | \def\sphinx@@multirow {% |
---|
248 | % The contents, which is a varwidth environment, has been captured in |
---|
249 | % \box0 (a \hbox). |
---|
250 | % We have with \sphinx@cellid an assigned unique id. The goal is to give |
---|
251 | % about the same height to all the involved rows. |
---|
252 | % For this Sphinx will insert a \sphinxtablestrut{cell_id} mark-up |
---|
253 | % in LaTeX file and the expansion of the latter will do the suitable thing. |
---|
254 | \dimen@\dp\z@ |
---|
255 | \dimen\tw@\ht\@arstrutbox |
---|
256 | \advance\dimen@\dimen\tw@ |
---|
257 | \advance\dimen\tw@\dp\@arstrutbox |
---|
258 | \count@=\dimen@ % type conversion dim -> int |
---|
259 | \count\tw@=\dimen\tw@ |
---|
260 | \divide\count@\count\tw@ % TeX division truncates |
---|
261 | \advance\dimen@-\count@\dimen\tw@ |
---|
262 | % 1300sp is about 0.02pt. For comparison a rule default width is 0.4pt. |
---|
263 | % (note that if \count@ holds 0, surely \dimen@>1300sp) |
---|
264 | \ifdim\dimen@>1300sp \advance\count@\@ne \fi |
---|
265 | % now \count@ holds the count L of needed "lines" |
---|
266 | % and \sphinx@nbofrows holds the number N of rows |
---|
267 | % we have L >= 1 and N >= 1 |
---|
268 | % if L is a multiple of N, ... clear what to do ! |
---|
269 | % else write L = qN + r, 1 <= r < N and we will |
---|
270 | % arrange for each row to have enough space for: |
---|
271 | % q+1 "lines" in each of the first r rows |
---|
272 | % q "lines" in each of the (N-r) bottom rows |
---|
273 | % for a total of (q+1) * r + q * (N-r) = q * N + r = L |
---|
274 | % It is possible that q == 0. |
---|
275 | \count\tw@\count@ |
---|
276 | % the TeX division truncates |
---|
277 | \divide\count\tw@\sphinx@nbofrows\relax |
---|
278 | \count4\count\tw@ % q |
---|
279 | \multiply\count\tw@\sphinx@nbofrows\relax |
---|
280 | \advance\count@-\count\tw@ % r |
---|
281 | \expandafter\xdef\csname sphinx@tablestrut_\sphinx@cellid\endcsname |
---|
282 | {\noexpand\sphinx@tablestrut{\the\count4}{\the\count@}{\sphinx@cellid}}% |
---|
283 | \dp\z@\z@ |
---|
284 | % this will use the real height if it is >\ht\@arstrutbox |
---|
285 | \sphinxtablestrut{\sphinx@cellid}\box\z@ |
---|
286 | \endgroup % group was opened in \sphinxmultirow |
---|
287 | }% |
---|
288 | \newcommand*\sphinxtablestrut[1]{% |
---|
289 | % #1 is a "cell_id", i.e. the id of a merged group of table cells |
---|
290 | \csname sphinx@tablestrut_#1\endcsname |
---|
291 | }% |
---|
292 | % LaTeX typesets the table row by row, hence each execution can do |
---|
293 | % an update for the next row. |
---|
294 | \newcommand*\sphinx@tablestrut[3]{\begingroup |
---|
295 | % #1 = q, #2 = (initially) r, #3 = cell_id, q+1 lines in first r rows |
---|
296 | % if #2 = 0, create space for max(q,1) table lines |
---|
297 | % if #2 > 0, create space for q+1 lines and decrement #2 |
---|
298 | \leavevmode |
---|
299 | \count@#1\relax |
---|
300 | \ifnum#2=\z@ |
---|
301 | \ifnum\count@=\z@\count@\@ne\fi |
---|
302 | \else |
---|
303 | % next row will be with a #2 decremented by one |
---|
304 | \expandafter\xdef\csname sphinx@tablestrut_#3\endcsname |
---|
305 | {\noexpand\sphinx@tablestrut{#1}{\the\numexpr#2-\@ne}{#3}}% |
---|
306 | \advance\count@\@ne |
---|
307 | \fi |
---|
308 | \vrule\@height\ht\@arstrutbox |
---|
309 | \@depth\dimexpr\count@\ht\@arstrutbox+\count@\dp\@arstrutbox-\ht\@arstrutbox\relax |
---|
310 | \@width\z@ |
---|
311 | \endgroup |
---|
312 | % we need this to avoid colour panels hiding bottom parts of multirow text |
---|
313 | \sphinx@hack@CT |
---|
314 | }% |
---|
315 | \endinput |
---|
316 | %% |
---|
317 | %% End of file `sphinxmulticell.sty'. |
---|