tabl-tsp.mkxl /size: 19 Kb    last modification: 2023-12-21 09:44
1%D \module
2%D   [       file=tabl-tsp,
3%D        version=2000.10.20,
4%D          title=\CONTEXT\ Table Macros,
5%D       subtitle=Splitting,
6%D         author=Hans Hagen,
7%D           date=\currentdate,
8%D      copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
9%C
10%C This module is part of the \CONTEXT\ macro||package and is
11%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
12%C details.
13
14\writestatus{loading}{ConTeXt Table Macros / Splitting}
15
16%D The code in this file is moved here from other places and needs a mkiv cleanup.
17%D As it mostly targets at tables the code lives in the tabl and page namespaces.
18
19% work in progress
20
21\unprotect
22
23%D Although the name resembles floats, and therefore this should be a page module,
24%D we decided to make it core functionality because the table code depends on it.
25%D Othrwise there would be too much overloading afterwards involved. Actually, the
26%D float part is rather generic and not that related to floats.
27
28% \splitfloat [settings] {\placetable[optional args]{test}} {content}
29
30%D When \type {inbetween} is made empty instead of the default \type {\page}, we
31%D will get delayed flushing and text may continue below the graphic.
32%D
33%D \starttyping
34%D \dorecurse{2}{\input tufte }
35%D
36%D \splitfloat[lines=auto,inbetween=]
37%D   {\placetable{\dorecurse{5}{test\recurselevel\endgraf}}}
38%D   {\bTABLE[split=yes]
39%D    \bTR \bTD 11 \eTD \bTD \input tufte \eTD \eTR
40%D    \bTR \bTD 12 \eTD \bTD \input zapf \eTD \eTR
41%D    \bTR \bTD 13 \eTD \bTD \input bryson \eTD \eTR
42%D    \bTR \bTD 14 \eTD \bTD test  \eTD \eTR
43%D    \bTR \bTD 21 \eTD \bTD \input tufte \eTD \eTR
44%D    \bTR \bTD 22 \eTD \bTD \input zapf \eTD \eTR
45%D    \bTR \bTD 23 \eTD \bTD \input bryson \eTD \eTR
46%D    \bTR \bTD 24 \eTD \bTD test  \eTD \eTR
47%D    \bTR \bTD 31 \eTD \bTD \input tufte \eTD \eTR
48%D    \bTR \bTD 32 \eTD \bTD \input zapf \eTD \eTR
49%D    \bTR \bTD 33 \eTD \bTD \input bryson \eTD \eTR
50%D    \bTR \bTD 34 \eTD \bTD test  \eTD \eTR
51%D    \eTABLE}
52%D
53%D \dorecurse{10}{\input tufte }
54%D \stoptyping
55
56\installcorenamespace{floatsplitting}
57
58\installdirectcommandhandler \??floatsplitting {floatsplitting} % \??floatsplitting
59
60\setupfloatsplitting
61  [\c!conversion=\v!character, % \v!romannumerals
62   \c!lines=3,
63   \c!before=,
64   \c!inbetween=\page,
65   \c!after=]
66
67\newconditional\splitfloatfirstdone
68\newconditional\somenextsplitofffloat
69\newconditional\splitfloatdone
70\newconditional\onlyonesplitofffloat   \onlyonesplitofffloat\conditionaltrue
71
72\newif   \ifinsidesplitfloat   % will become conditional
73\newcount\noffloatssplits
74\newtoks \everysplitfloatsetup
75
76\mutable\let\extrasplitfloatlines \!!zerocount
77\mutable\let\splitfloatfinalizer  \relax
78
79\mutable\lettonothing\splitfloatcommand
80\mutable\lettonothing\floatcaptionsuffix
81
82\permanent\tolerant\protected\def\splitfloat[#S#1]#:#2% nog dubbele refs
83  {\bgroup
84   \global\splitfloatdone\conditionalfalse
85   \aftergroup\page_split_float_check
86   \insidefloattrue
87   \insidesplitfloattrue
88   \setupcurrentfloatsplitting[#1]%
89   \global\noffloatssplits\zerocount
90   \let\floatcaptionsuffix\page_split_float_suffix
91   \edef\extrasplitfloatlines{\floatsplittingparameter\c!lines}%
92   \expand\everysplitfloatsetup
93   \def\splitfloatcommand{#2}%
94   \global\onlyonesplitofffloat\conditionaltrue
95   \global\somenextsplitofffloat\conditionalfalse
96   \page_floats_push_saved
97   \floatsplittingparameter\c!before
98   \let\next} % \bgroup
99
100\protected\def\page_split_float_suffix
101  {\begingroup
102   \usefloatsplittingstyleandcolor\c!style\c!color % only the suffix
103   \convertnumber{\floatsplittingparameter\c!conversion}\noffloatssplits
104   \endgroup}
105
106\protected\def\page_split_float_check
107  {\ifconditional\splitfloatdone
108    %\splitfloatfinalizer % a weird place (could interfere with flushing)
109   \else
110     \blank
111     \begingroup
112     \tttf \dontleavehmode \getmessage\m!floatblocks{13}\empty
113     \endgroup
114     \blank
115     \showmessage\m!floatblocks{13}\empty
116   \fi
117   \splitfloatfinalizer} % a weird place (could interfere with flushing)
118
119\def\page_split_float_process % nextbox
120  {\ifinsidesplitfloat
121     \expandafter\page_split_float_process_yes
122   \else
123     \expandafter\page_split_float_process_nop
124   \fi}
125
126\def\page_split_float_process_yes
127  {\dowithnextboxcs\page_split_float_process_finish\vbox}
128
129\def\page_split_float_process_finish
130  {\forgetall
131   \dontcomplain
132   \global\splitfloatdone\conditionaltrue
133 % \nodelocationmode\zerocount % bypass auto-renumbering
134   \global\advanceby\noffloatssplits\plusone
135   \ifcase\noffloatssplits\relax \or
136     \ifconditional\onlyonesplitofffloat
137       \lettonothing\floatcaptionsuffix
138     \fi
139   \fi
140   \bgroup
141     \ifconditional\somenextsplitofffloat
142       \notesenabledfalse % best here, experimental, brrr; test with note in caption
143     \fi
144     \splitfloatcommand{\box\nextbox}%
145   \egroup
146   \ifconditional\somenextsplitofffloat
147     \edef\p_inbetween{\floatsplittingparameter\c!inbetween}%
148     \ifempty\p_inbetween
149       \ifconditional\splitfloatfirstdone\else\page\fi
150     \else
151       \p_inbetween
152     \fi
153   \else
154     \floatsplittingparameter\c!after
155     \page_floats_pop_saved
156     \page_floats_flush_saved
157   \fi
158   \global\splitfloatfirstdone\conditionaltrue}
159
160\def\page_split_float_process_nop
161  {\dowithnextboxcs\page_split_float_process_nop_finish\vbox}
162
163\def\page_split_float_process_nop_finish
164  {\forgetall
165   \dontcomplain
166   \box\nextbox % maybe an option to unvbox
167   \global\splitfloatfirstdone\conditionaltrue}
168
169\def\page_split_float_check_content#1% box
170  {\ifinsidesplitfloat
171   % \ifdim\ht#1=\zeropoint % funny: \ifcase does not check for overflow
172     \ifcase\ht#1\relax
173       \global\somenextsplitofffloat\conditionalfalse
174     \else
175       \global\somenextsplitofffloat\conditionaltrue
176       \global\onlyonesplitofffloat\conditionalfalse
177     \fi
178   \fi}
179
180\def\page_split_float_check_caption#1% depends on page-flt .. pretty messy
181  {\edef\extrasplitfloatlines{\extrasplitfloatlines}%
182   \ifx\extrasplitfloatlines\v!auto
183      \bgroup
184      \forcelocalfloats
185      \setuplocalfloats[\c!before=,\c!after=,\c!inbetween=]%
186      % This controls samepage resetting too but it also messes up the numbering
187      % so I need another fix.
188%       \settrialtypesetting
189      \splitfloatcommand{\hbox to #1{\strut}}% dummy line
190%       \resettrialtypesetting
191      \setbox\scratchbox\vbox{\flushlocalfloats}% \vpack ?
192      \getnoflines{\ht\scratchbox}%
193      \resetlocalfloats
194      \advanceby\noflines\minusone % compensate dummy line
195      \normalexpanded{\egroup\noexpand\edef\noexpand\extrasplitfloatlines{\the\noflines}}%
196      \global\usesamefloatnumber\conditionaltrue
197   \orelse\ifchknum\extrasplitfloatlines\else
198     \def\extrasplitfloatlines{1}
199   \fi}
200
201\permanent\protected\def\doifnotinsidesplitfloat
202  {\ifinsidesplitfloat
203     \expandafter\gobbleoneargument
204   \fi}
205
206%D Table splitter, on top of previous code:
207
208% todo: keep tail to rest, so we need a lookahead
209
210\newbox        \b_split_content
211\newbox        \b_split_result    % watch out, semi public, accessed in cs-*
212\newbox        \b_split_head
213\newbox        \b_split_next
214\newbox        \b_split_tail
215
216\newtoks       \t_split_before_result
217\newtoks       \t_split_after_result
218\newtoks       \t_split_before
219\newtoks       \t_split_inbetween
220\newtoks       \t_split_after
221\newtoks       \t_split_section
222\newtoks       \everyresettsplit
223
224\newinteger    \c_split_minimum_free_lines
225
226\newdimension  \d_split_minimum_free_space
227\newdimension  \d_split_available_height
228\newdimension  \d_split_inbetween_height
229
230\newconditional\c_tabl_split_done
231\newconditional\c_tabl_split_head
232\newconditional\c_tabl_split_full
233
234\newconditional\tabl_split_forced_page
235
236% \permanent\protected\def\lastsplithtdp{\htdp\b_split_result}
237
238\appendtoks
239   \c_split_minimum_free_lines\zerocount
240   \d_split_minimum_free_space\zeropoint
241   \setbox\b_split_content    \emptyvbox
242   \setbox\b_split_result     \emptyvbox
243   \setbox\b_split_head       \emptyvbox
244   \setbox\b_split_next       \emptyvbox
245   \setbox\b_split_tail       \emptyvbox
246   \t_split_before_result     \emptytoks
247   \t_split_after_result      \emptytoks
248   \t_split_inbetween         \emptytoks
249   \t_split_before            \emptytoks
250   \t_split_after             \emptytoks
251   \t_split_section           \emptytoks
252   \let\postprocesstsplit     \donothing
253\to \everyresettsplit
254
255\mutable\let\postprocesstsplit\donothing
256
257\permanent\protected\def\resettsplit
258  {\expand\everyresettsplit}
259
260\resettsplit
261
262\mutable\def\tsplitdirectwidth{\hsize}
263
264\permanent\protected\def\handletsplit
265  {\page_split_float_check_caption{\wd\b_split_content}%
266   \global\splitfloatfirstdone\conditionalfalse
267   \testpagesync % new, sync, but still tricky
268     [\the\c_split_minimum_free_lines]
269     [\dimexpr\d_split_minimum_free_space+\extrasplitfloatlines\lineheight\relax]%
270   \setbox\scratchbox\vbox{\expand\t_split_inbetween}%
271   \d_split_inbetween_height\htdp\scratchbox
272   \c_tabl_split_done\conditionalfalse
273   \localcontrolledrepeating
274     {\tabl_split_loop_body}%
275   \global\usesamefloatnumber\conditionalfalse % new, prevent next increment
276   \global\splitfloatfirstdone\conditionalfalse} % we can use this one for tests
277
278\newbox\b_split_result_saved
279
280\def\tabl_split_loop_body
281  {\ifinsidecolumns
282     % brrr, assumes empty columns
283     \global\splitfloatfirstdone\conditionalfalse
284     \d_split_available_height\textheight
285     \c_tabl_split_full\conditionaltrue
286   \else
287     \ifconditional\splitfloatfirstdone
288       \d_split_available_height\textheight
289       \c_tabl_split_full\conditionaltrue
290     \orelse\ifdim\pagegoal<\maxdimen
291       \d_split_available_height\dimexpr\pagegoal-\pagetotal\relax
292       \c_tabl_split_full\conditionalfalse
293     \else
294       \d_split_available_height\textheight
295       \c_tabl_split_full\conditionaltrue
296     \fi
297   \fi
298   \d_split_available_height \dimexpr
299      \d_split_available_height
300     -\d_split_inbetween_height
301     -\d_split_minimum_free_space
302     -\extrasplitfloatlines\lineheight
303   \relax
304   \ifdim\htdp\b_split_tail>\zeropoint
305     \advanceby\d_split_available_height-\htdp\b_split_tail
306   \fi
307   \setbox\b_split_result\vbox
308     {\ifdim\ht\b_split_head>\zeropoint
309        \unvcopy\b_split_head
310        \expand\t_split_section
311        \expand\t_split_inbetween
312      \fi}%
313   \ifconditional\c_tabl_split_done \else
314     \ifdim\ht\b_split_next>\zeropoint
315       \setbox\b_split_head\box\b_split_next
316     \fi
317   \fi
318   \c_tabl_split_done\conditionaltrue
319   \ifdim\ht\b_split_result>\zeropoint
320     \c_tabl_split_head\conditionaltrue  % table head
321   \else
322     \c_tabl_split_head\conditionalfalse % no tablehead
323   \fi
324   \splittopskip\zeroskip
325   \ifvoid\b_split_result_saved\else
326     \setbox\b_split_result\box\b_split_result_saved
327     \c_tabl_split_head\conditionaltrue % no tablehead
328     \global\tabl_split_forced_page\conditionalfalse
329   \fi
330   \localcontrolledrepeating
331     {\setbox\scratchbox\vsplit\b_split_content to \onepoint % \lineheight
332      \setbox\scratchbox\vbox % \vpack
333        {\unvbox\scratchbox
334         \setbox\scratchbox\vbox % \vpack
335           {\splitdiscards
336            \ifnum\lastpenalty>-\plustenthousand\else
337               % so that \bTR[before=\page] works
338               \global\tabl_split_forced_page\conditionaltrue
339            \fi}}%
340      \ifconditional\tabl_split_forced_page
341        \global\tabl_split_forced_page\conditionalfalse
342        \setbox\b_split_result\vbox
343          {\unvbox\b_split_result
344           \expand\t_split_inbetween
345           \unvbox\scratchbox}%
346        \quitloop
347      \orelse\ifdim\dimexpr\d_split_available_height-\htdp\scratchbox-\htdp\b_split_result\relax>\zeropoint
348        \setbox\b_split_result\vbox
349          {\unvbox\b_split_result
350           \expand\t_split_inbetween
351           \unvbox\scratchbox}%
352        \ifvoid\b_split_content
353          \quitloop
354        \fi
355      \orelse\ifconditional\c_tabl_split_head
356        % we only have a tablehead so far
357        \global\setbox\b_split_result_saved\vbox{\unvbox\b_split_result\unvbox\scratchbox}% \vpack
358        \quitloop
359      \orelse\ifconditional\c_tabl_split_full
360        % we have text height available, but the (one) cell is too
361        % large to fit, so, in order to avoid loops/deadcycles we do:
362        \setbox\b_split_result\vbox
363          {\unvbox\b_split_result
364           \expand\t_split_inbetween
365           \unvbox\scratchbox}%
366        \quitloop
367      \else
368        \setbox\b_split_content\vbox
369          {\unvbox\scratchbox
370           \expand\t_split_inbetween
371           \ifvoid\b_split_content\else\unvbox\b_split_content\fi}%
372        \quitloop
373      \fi
374      \c_tabl_split_head\conditionalfalse
375      \c_tabl_split_full\conditionalfalse}%
376   \postprocesstsplit
377   \page_split_float_check_content\b_split_content
378   \ifvoid\b_split_content
379     \setbox\b_split_result\vbox
380       {\unvbox\b_split_result
381        \expand\t_split_inbetween
382        \unvcopy\b_split_tail}%
383     \page_split_float_process{\expand\t_split_before_result\box\b_split_result\expand\t_split_after_result}%
384     \doifnotinsidesplitfloat{\expand\t_split_after}%
385     \endgraf
386     \quitloop
387   \else
388     % hack
389     \ifdim\pagegoal<\maxdimen
390       \pagegoal\dimexpr\pagegoal+\lineheight\relax % etex
391     \fi
392     % brrr
393     \ifdim\ht\b_split_result>\zeropoint
394       \setbox\b_split_result\vbox
395         {\unvbox\b_split_result
396          \expand\t_split_inbetween
397          \unvcopy\b_split_tail}%
398       \page_split_float_process{\expand\t_split_before_result\box\b_split_result\expand\t_split_after_result}%
399       \doifnotinsidesplitfloat{\expand\t_split_after}%
400       \endgraf
401       \global\usesamefloatnumber\conditionaltrue % new, prevent next increment
402     \fi
403     \ifinsidecolumns
404       \goodbreak % was \doifnotinsidesplitfloat\goodbreak
405     \else
406       \page      % was \doifnotinsidesplitfloat\page
407     \fi
408   \fi}
409
410%D The next one assumes that the split takes place elsewhere. This is used in
411%D xtables.
412
413\aliased\let\resetdirecttsplit\resettsplit
414
415\mutable\let\tsplitdirectsplitter\relax
416
417\permanent\protected\def\handledirecttsplit
418  {\page_split_float_check_caption{\tsplitdirectwidth}%
419   \global\splitfloatfirstdone\conditionalfalse
420   \testpagesync % new, sync, but still tricky
421     [\the\c_split_minimum_free_lines]
422     [\dimexpr\d_split_minimum_free_space+\extrasplitfloatlines\lineheight\relax]%
423   \localcontrolledrepeating
424     {\tabl_split_direct_loop_body}%
425   \global\usesamefloatnumber\conditionalfalse   % new, prevent next increment
426   \global\splitfloatfirstdone\conditionalfalse} % we can use this one for tests
427
428\newconditional\splitfloatfixedheight
429
430\def\splitfloatheight{\textheight}
431
432\def\tabl_split_direct_loop_body
433  {\ifconditional\splitfloatfixedheight
434     \d_split_available_height\splitfloatheight
435   \orelse\ifinsidecolumns
436     \global\splitfloatfirstdone\conditionalfalse
437     \d_split_available_height\textheight
438   \orelse\ifconditional\splitfloatfirstdone
439     \d_split_available_height\textheight
440   \orelse\ifdim\pagegoal<\maxdimen
441     \d_split_available_height\dimexpr\pagegoal-\pagetotal\relax
442   \else
443     \d_split_available_height\textheight
444   \fi
445   \d_split_available_height\dimexpr
446      \d_split_available_height
447     -\d_split_minimum_free_space
448     -\extrasplitfloatlines\lineheight
449   \relax
450   \tsplitdirectsplitter\d_split_available_height % also sets state
451   \ifdim\ht\b_split_result>\zeropoint
452     \ifconditional\somenextsplitofffloat
453       \global\onlyonesplitofffloat\conditionalfalse
454     \fi
455     \ifconditional\splitfloatfixedheight
456       % not relevant
457     \orelse\ifdim\pagegoal<\maxdimen
458       \pagegoal\dimexpr\pagegoal+\lineheight\relax % etex
459     \fi
460     \page_split_float_process{\expand\t_split_before_result\box\b_split_result\expand\t_split_after_result}%
461     \global\usesamefloatnumber\conditionaltrue % new, prevent next increment
462     \endgraf
463     \ifconditional\somenextsplitofffloat
464       \ifconditional\splitfloatfixedheight
465         \page
466       \orelse\ifinsidecolumns
467         \goodbreak
468       \else
469         \page
470       \fi
471     \fi
472     \global\splitfloatfirstdone\conditionaltrue
473   \orelse\ifconditional\somenextsplitofffloat
474     \ifconditional\splitfloatfixedheight
475       \page
476     \orelse\ifinsidecolumns
477       \goodbreak
478     \else
479       \page % no room
480     \fi
481   \else
482     \quitloop
483   \fi}
484
485%D Maybe handy:
486%D
487%D \starttyping
488%D \splitfloat
489%D   {\placefigure{some caption}}
490%D   {\startsplittext
491%D    \typefile[option=TEX,before=,after=]{oeps.tex}
492%D    \stopsplittext}
493%D \stoptyping
494
495\permanent\def\handlesplittext#1%
496  {\setbox\b_split_result\vbox
497     {\vsplit\b_split_content to \dimexpr#1-\lineheight\relax}}
498
499\permanent\protected\def\startsplittext
500  {\begingroup
501   \resettsplit
502   \c_split_minimum_free_lines\zerocount
503   \d_split_minimum_free_space\zeropoint
504   \let\extrasplitfloatlines  \!!plusone
505   \let\tsplitdirectsplitter  \handlesplittext
506   \setbox\b_split_content\vbox\bgroup
507   \insidefloattrue}
508
509\permanent\protected\def\stopsplittext
510  {\egroup
511   \handledirecttsplit
512   \endgroup}
513
514\protect \endinput
515
516% test cases
517
518% \setupTABLE[split=repeat]
519%
520% \input tufte \endgraf
521% \splitfloat[lines=11]
522%   {\placetable{\dorecurse{10}{test\recurselevel\endgraf}}}
523%   {\bTABLE\dorecurse{100}{\bTR \bTD test \eTD \eTR}\eTABLE}
524% \input tufte \page
525%
526% \input tufte \endgraf
527% \splitfloat[lines=0]
528%   {}
529%   {\bTABLE\dorecurse{100}{\bTR \bTD test \eTD \eTR}\eTABLE}
530% \input tufte \endgraf \page
531%
532% \input tufte \endgraf
533% \bTABLE\dorecurse{100}{\bTR \bTD test \eTD \eTR}\eTABLE
534% \input tufte \page
535
536% \setuptabulate[split=yes]
537%
538% \input tufte \endgraf
539% \splitfloat[lines=11]
540%   {\placetable{\dorecurse{10}{test\recurselevel\endgraf}}}
541%   {\starttabulate\dorecurse{200}{\NC test \NC test \NC \NR}\stoptabulate}
542% \input tufte \page
543%
544% \input tufte \endgraf
545% \splitfloat[lines=0]
546%   {}
547%   {\starttabulate\dorecurse{200}{\NC test \NC test \NC \NR}\stoptabulate}
548% \input tufte \page
549%
550% \input tufte \endgraf
551% \starttabulate\dorecurse{200}{\NC test \NC test \NC \NR}\stoptabulate
552% \input tufte \page
553
554% \setuptables[split=yes]
555%
556% \newtoks\TestToks
557%
558% \TestToks\emptytoks
559% \appendtoks\starttablehead\to\TestToks
560% \dorecurse{3}{\appendtoks\VL head \VL head \VL \SR\to\TestToks}
561% \appendtoks\stoptablehead\to\TestToks
562% \appendtoks\starttabletail\to\TestToks
563% \dorecurse{3}{\appendtoks\VL tail \VL tail \VL \SR\to\TestToks}
564% \appendtoks\stoptabletail\to\TestToks
565% \appendtoks\starttables[|c|c|]\to\TestToks
566% \dorecurse{100}{\appendtoks\VL test \VL test \VL \SR\to\TestToks}
567% \appendtoks\stoptables\to\TestToks
568%
569% \input tufte \endgraf
570% \splitfloat[lines=auto] % [lines=11]
571%   {\placetable{\dorecurse{10}{test\recurselevel\endgraf}}}
572%   {\the\TestToks}
573% \input tufte \page
574%
575% \input tufte \endgraf
576% \splitfloat[lines=0]
577%   {}
578%   {\the\TestToks}
579% \input tufte \page
580%
581% \input tufte \endgraf
582% \the\TestToks
583% \input tufte \page
584%
585% multiple floats
586%
587% \starttext
588%   \dorecurse{3}{\input tufte } \endgraf
589%   \dorecurse{5}{\placefigure{}{\framed[height=.5\textheight]{}}}
590%   \splitfloat[lines=auto,inbetween=]
591%     {\placetable{\dorecurse{5}{test\recurselevel\endgraf}}}
592%     {\bTABLE[split=yes]
593%      \bTR \bTD 11 \eTD \bTD \input tufte \eTD \eTR
594%      \bTR \bTD 12 \eTD \bTD \input zapf \eTD \eTR
595%      \bTR \bTD 13 \eTD \bTD \input bryson \eTD \eTR
596%      \bTR \bTD 14 \eTD \bTD test  \eTD \eTR
597%      \bTR \bTD 21 \eTD \bTD \input tufte \eTD \eTR
598%      \bTR \bTD 22 \eTD \bTD \input zapf \eTD \eTR
599%      \bTR \bTD 23 \eTD \bTD \input bryson \eTD \eTR
600%      \bTR \bTD 24 \eTD \bTD test  \eTD \eTR
601%      \bTR \bTD 31 \eTD \bTD \input tufte \eTD \eTR
602%      \bTR \bTD 32 \eTD \bTD \input zapf \eTD \eTR
603%      \bTR \bTD 33 \eTD \bTD \input bryson \eTD \eTR
604%      \bTR \bTD 34 \eTD \bTD test  \eTD \eTR
605%      \eTABLE}
606%   \dorecurse{10}{\input tufte }
607% \stoptext
608