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