pack-com.mkxl /size: 33 Kb    last modification: 2021-10-28 13:51
1%D \module
2%D   [       file=pack-com, % used to be in core-mis,
3%D        version=20120111,
4%D          title=\CONTEXT\ Packing Macros,
5%D       subtitle=Combinations,
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 Packaging Macros / Combinations}
15
16\unprotect
17
18% \startfloatcombination will be redone ... we can decouple the floatcontent
19% and caption and pass them to combinations so that we get better fit when the
20% caption is wider than the float, testcase:
21%
22% \startfloatcombination [2*2]
23%    \placefigure[local]{alpha}{\externalfigure[cow.pdf][width=1cm]}%
24%    \placefigure[local]{beta} {\externalfigure[cow.pdf][width=2cm]}%
25%    \placefigure[local]{gamma}{\externalfigure[cow.pdf][width=3cm]}
26%    \placefigure[local]{delta}{\externalfigure[cow.pdf][width=4cm]}
27% \stopfloatcombination
28
29%D We could of course map combinations onto one of the table mechanisms but as it
30%D has served us well for ages we keep this one. The code has been cleaned up a bit
31%D and mkiv'd.
32%D
33%D Okay ... I might luafy this one eventually.
34
35% \startcombination      {alpha} {a} {beta} {b} \stopcombination
36% \startcombination[2*1] {alpha} {a} {beta} {b} \stopcombination
37% \startcombination[1*2] {alpha} {a} {beta} {b} \stopcombination
38% \startcombination[2]   {alpha} {a} {beta} {b} \stopcombination
39% \startcombination[2]   \combination {alpha} {a} \combination{beta} {b} \stopcombination
40
41%D We do support some structure but the order matters and currently it's only window
42%D dressing:
43
44%D \starttyping
45%D \let\startcontent\bgroup
46%D \let\stopcontent \egroup
47%D \let\startcaption\bgroup
48%D \let\stopcaption \egroup
49%D \stoptyping
50%D
51%D Of course we should have started with more structure as it would simply the code.
52%D
53%D \starttyping
54%D \startcombination
55%D     \startcontent
56%D         \externalfigure[cow]
57%D     \stopcontent
58%D     \startcaption
59%D         Some cow.
60%D     \stopcaption
61%D     \startcontent
62%D         \externalfigure[cow]
63%D     \stopcontent
64%D     \startcaption
65%D         The same cow.
66%D     \stopcaption
67%D \stopcombination
68%D \stoptyping
69
70\ifdefined\dotagcombination \else \aliased\let\dotagcombination\relax \fi
71
72\newsystemmode{combination}
73
74\appendtoks
75    \globalresetsystemmode{combination}%
76\to \everyinsidefloat
77
78\newcount\c_pack_combinations_nesting  % local
79
80\newcount\c_pack_combinations_x        % global
81\newcount\c_pack_combinations_y        % global
82\newcount\c_pack_combinations_max      % global
83
84\newdimen\d_pack_combinations_ht       % global
85
86\newbox  \b_pack_combinations_captions % global % can go
87\newbox  \b_pack_combinations_temp     % global % can go
88\newbox  \b_pack_combinations_content  % local
89\newbox  \b_pack_combinations_caption  % local
90
91\installcorenamespace{combination}
92
93\installcommandhandler \??combination {combination} \??combination
94
95\initializeboxstack{\??combination captions}
96\initializeboxstack{\??combination temp}
97
98\newcount\c_pack_combinations_x_saved
99\newcount\c_pack_combinations_y_saved
100\newcount\c_pack_combinations_max_saved
101
102\newdimen\d_pack_combinations_ht_saved
103
104\newbox  \b_pack_combinations_captions_saved
105\newbox  \b_pack_combinations_temp_saved
106\newbox  \b_pack_combinations_content_saved
107\newbox  \b_pack_combinations_caption_saved
108
109\setfalse\c_strc_constructions_define_commands
110
111\def\pack_combinations_push
112  {\advance\c_pack_combinations_nesting\plusone
113   \ifnum\c_pack_combinations_nesting>\plusone
114     \c_pack_combinations_x_saved  \c_pack_combinations_x
115     \c_pack_combinations_y_saved  \c_pack_combinations_y
116     \c_pack_combinations_max_saved\c_pack_combinations_max
117     \d_pack_combinations_ht_saved \d_pack_combinations_ht
118     \setbox\b_pack_combinations_captions_saved\box\b_pack_combinations_captions
119     \setbox\b_pack_combinations_temp_saved    \box\b_pack_combinations_temp
120     \setbox\b_pack_combinations_content_saved \box\b_pack_combinations_content
121     \setbox\b_pack_combinations_caption_saved \box\b_pack_combinations_caption
122   \else
123     \globalsetsystemmode{combination}% why global
124   \fi}
125
126\def\pack_combinations_pop
127  {\ifnum\c_pack_combinations_nesting>\plusone
128     \global\c_pack_combinations_x  \c_pack_combinations_x_saved
129     \global\c_pack_combinations_y  \c_pack_combinations_y_saved
130     \global\c_pack_combinations_max\c_pack_combinations_max_saved
131     \global\d_pack_combinations_ht \d_pack_combinations_ht_saved
132     \global\setbox\b_pack_combinations_captions\box\b_pack_combinations_captions_saved
133     \global\setbox\b_pack_combinations_temp    \box\b_pack_combinations_temp_saved
134     \setbox\b_pack_combinations_content        \box\b_pack_combinations_content_saved
135     \setbox\b_pack_combinations_caption        \box\b_pack_combinations_caption_saved
136   \else
137     \globalresetsystemmode{combination}% why global
138   \fi
139   \advance\c_pack_combinations_nesting\minusone}
140
141\definelabel
142  [\v!combination] % handy for configuring
143  [\c!numberconversion=\v!character,
144   \c!text=]
145
146\settrue\c_strc_constructions_define_commands
147
148\setupcombination
149  [\c!width=\v!fit,
150   \c!height=\v!fit,
151   \c!distance=\emwidth,
152   \c!location=\v!bottom, % can be something {top,left}
153   \c!before=\blank,
154   \c!after=,
155   \c!inbetween={\blank[\v!medium]},
156  %\c!style=,
157  %\c!color=,
158   \c!nx=2, % new
159   \c!ny=1, % new
160   \c!align=\v!middle]
161
162\let\setupcombinations\setupcombination % for the moment (we might distinguish)
163
164\installcorenamespace{combinationlocation}
165\installcorenamespace{combinationalternative}
166
167\appendtoks
168    \setfalse\c_strc_constructions_define_commands
169    \normalexpanded
170      {\definelabel
171         [\v!combination:\currentcombination]%
172         [\v!combination\ifempty\currentcombinationparent\else:\currentcombinationparent\fi]}%
173         [\s!counter=\currentcombination,\c!levels=1]%
174    \settrue\c_strc_constructions_define_commands
175\to \everydefinecombination
176
177\setvalue{\??combinationlocation\v!left  }{\let\m_pack_combinations_leftfiller\relax}
178\setvalue{\??combinationlocation\v!right }{\let\m_pack_combinations_rightfiller\relax}
179\setvalue{\??combinationlocation\v!top   }{\let\m_pack_combinations_valigner\depthonlybox}
180\setvalue{\??combinationlocation\v!middle}{\let\m_pack_combinations_valigner\halfwaybox}
181
182\def\pack_combinations_location_reset
183  {\let\m_pack_combinations_rightfiller\hfil
184   \let\m_pack_combinations_leftfiller \hfil
185   \let\m_pack_combinations_valigner   \firstofoneargument}
186
187\pack_combinations_location_reset
188
189\def\pack_combinations_location_step#1%
190  {\csname\??combinationlocation#1\endcsname}
191
192% formally ok:
193%
194% \protected\def\stopcombination
195%   {\egroup
196%    \egroup}
197%
198% more robust:
199%
200% \protected\def\stopcombination
201%   {{}{}{}{}{}{}{}{}% catches (at most 4) missing entries
202%    \egroup
203%    \egroup}
204%
205% even better:
206%
207% \protected\def\stopcombination
208%   {\bgroup
209%    \scratchtoks{{}}%
210%    \dorecurse\c_pack_combinations_y
211%      {\toksapp{{}{}}}%
212%    \expandafter\egroup\the\scratchtoks
213%    \egroup
214%    \dostoptagged
215%    \egroup}
216%
217% faster
218
219\ifdefined\startcontent \else \aliased\let\startcontent\relax \fi
220\ifdefined\stopcontent  \else \aliased\let\stopcontent \relax \fi
221\ifdefined\startcaption \else \aliased\let\startcaption\relax \fi
222\ifdefined\stopcaption  \else \aliased\let\stopcaption \relax \fi
223
224\protected\def\pack_common_content_start{\bgroup\ignorespaces}
225\protected\def\pack_common_content_stop {\removeunwantedspaces\egroup}
226\protected\def\pack_common_caption_start{\bgroup\ignorespaces}
227\protected\def\pack_common_caption_stop {\removeunwantedspaces\egroup}
228
229\newtoks\everycombination
230
231\aliased\let\combination\empty
232
233\let\p_nx_ny\empty
234
235\permanent\protected\def\stopcombination
236  {\bgroup\normalexpanded{\egroup{}\ntimes{{}{}}\c_pack_combinations_y}% brr
237   \dostoptagged
238   \egroup
239   \pack_combinations_pop
240   \egroup}
241
242\permanent\tolerant\protected\def\startcombination[#1]#*[#2]% can be simplified
243  {\bgroup % so we can grab a group
244   \pack_combinations_push
245   \edef\currentcombination{#1}%
246   \edef\p_nx_ny{#2}%
247   %
248   \ifempty\p_nx_ny
249     \ifcondition\validassignment{#1}%
250       \let\currentcombination\empty
251       \setupcurrentcombination[#1]%
252       \edef\p_nx_ny{\combinationparameter\c!nx*\combinationparameter\c!ny*}%
253     \else
254       \doifelseinstring{*}\currentcombination
255         {\edef\p_nx_ny{\currentcombination*\plusone*}%
256          \let\currentcombination\empty}
257         {\doifelsenumber\currentcombination
258            {\edef\p_nx_ny{\currentcombination*\plusone*}%
259             \let\currentcombination\empty}
260            {\edef\p_nx_ny{\combinationparameter\c!nx*\combinationparameter\c!ny*}}}%
261     \fi
262   \else
263     \ifcondition\validassignment{#2}%
264       \setupcurrentcombination[#2]%
265       \edef\p_nx_ny{\combinationparameter\c!nx*\combinationparameter\c!ny*}%
266     \else
267       \edef\p_nx_ny{\p_nx_ny*\plusone*}%
268     \fi
269   \fi
270   %
271% test first:
272%
273%    \ifempty\p_nx_ny
274%      \ifhastok={#1}%
275%        \let\currentcombination\empty
276%        \setupcurrentcombination[#1]%
277%        \edef\p_nx_ny{\combinationparameter\c!nx*\combinationparameter\c!ny*}%
278%      \orelse\ifhastok*{\currentcombination}%
279%        \edef\p_nx_ny{\currentcombination*\plusone*}%
280%        \let\currentcombination\empty
281%      \orelse\ifchknum\currentcombination\or
282%        \edef\p_nx_ny{\currentcombination*\plusone*}%
283%        \let\currentcombination\empty
284%      \else
285%        \edef\p_nx_ny{\combinationparameter\c!nx*\combinationparameter\c!ny*}%
286%      \fi
287%    \orelse\ifhastok={#2}%
288%      \setupcurrentcombination[#2]%
289%      \edef\p_nx_ny{\combinationparameter\c!nx*\combinationparameter\c!ny*}%
290%    \else
291%      \edef\p_nx_ny{\p_nx_ny*\plusone*}%
292%    \fi
293   %
294   \forgetall
295   %
296   \the\everycombination
297   %
298   \enforced\let\startcontent\pack_common_content_start
299   \enforced\let\stopcontent \pack_common_content_stop
300   \enforced\let\startcaption\pack_common_caption_start
301   \enforced\let\stopcaption \pack_common_caption_stop
302   %
303   \edef\p_height  {\combinationparameter\c!height}%
304   \edef\p_width   {\combinationparameter\c!width}%
305   \edef\p_location{\combinationparameter\c!location}%
306   \edef\p_distance{\combinationparameter\c!distance}%
307   %
308   \pack_combinations_location_reset
309   \rawprocesscommacommand[\p_location]\pack_combinations_location_step
310   %
311   \dostarttaggedchained\t!combination\currentcombination\??combination
312   \vbox \ifx\p_height\v!fit\else to \p_height \fi \bgroup
313   \enforced\let\combination\empty % permits \combination{}{} handy for cld
314   \normalexpanded{\pack_combinations_start_indeed[\p_nx_ny]}}
315
316\let\pack_combinations_check_x_y\relax
317
318\protected\def\pack_combinations_start_indeed[#1*#2*#3]%
319  {\global\c_pack_combinations_x#1\relax
320   \global\c_pack_combinations_y#2\relax
321   \setexpandedcombinationparameter\c!nx{\the\c_pack_combinations_x}% in case we access it
322   \setexpandedcombinationparameter\c!ny{\the\c_pack_combinations_y}% in case we access it
323   \pack_combinations_check_x_y
324   \dotagcombination
325   \global\setbox\b_pack_combinations_captions\emptybox
326   \global\c_pack_combinations_max\c_pack_combinations_x
327   \multiply\c_pack_combinations_y\c_pack_combinations_x
328   \tabskip\zeropoint
329   \halign \ifx\p_width\v!fit\else to \p_width \fi \bgroup % repetitive preamble
330 % \halign noskips \ifx\p_width\v!fit\else to \p_width \fi \bgroup % repetitive preamble
331   \aligntab
332   \m_pack_combinations_leftfiller
333   \aligncontent
334   \m_pack_combinations_rightfiller
335   \aligntab
336   \tabskip\zeropoint \s!plus 1fill % \fillskip
337   \aligncontent
338   \cr
339   \pack_combinations_pickup}
340
341%D I've first considered using a constructor directly but it's more overhead and
342%D some settings conflict with already used combination settings so instead we plug
343%D in labels. This also permits extensions later on.
344
345\appendtoks
346    \edef\p_pack_combinations_alternative{\combinationparameter\c!alternative}%
347\to \everydefinecombination
348
349\def\pack_combinations_pickup
350  {\dostarttagged\t!combinationpair\empty     % better make this text
351   \dostarttagged\t!combinationcontent\empty
352   \expandafterpars\pack_combinations_pickup_content_indeed}
353
354\def\pack_combinations_pickup_content_indeed
355  {\dowithnextboxcs\pack_combinations_pickup_content\hbox}
356
357\def\pack_combinations_pickup_content % we want to add struts but still ignore an empty box
358  {\dostoptagged
359   \setbox\b_pack_combinations_content\box\nextbox
360   \dostarttagged\t!combinationcaption\empty
361   \expandnamespacemacro\??combinationalternative\p_pack_combinations_alternative\v!text}
362
363\setvalue{\??combinationalternative\v!text}%
364  {\expandafterpars\pack_combinations_alternative_text_indeed}
365
366\setvalue{\??combinationalternative\v!label}%
367  {\expandafterpars\pack_combinations_alternative_label_indeed}
368
369\def\pack_combinations_alternative_text_indeed
370  {\dowithnextboxcs\pack_combinations_pickup_caption\vtop\bgroup
371     \afterassignment\pack_combinations_caption_first
372     \let\nexttoken=}
373
374\def\pack_combinations_alternative_label_indeed
375  {\dowithnextboxcs\pack_combinations_pickup_caption\vtop\bgroup
376     \hsize\wd\b_pack_combinations_content
377     \usealignparameter\combinationparameter
378     \usecombinationstyleandcolor\c!style\c!color
379     \begstrut
380       \normalexpanded{\strc_labels_command[\v!combination\ifempty\currentcombination\else:\currentcombination\fi]}%
381     \endstrut
382   \egroup}
383
384\appendtoks
385    \edef\p_pack_combinations_alternative{\combinationparameter\c!alternative}%
386    \ifx\p_pack_combinations_alternative\v!label
387      \edef\p_continue{\combinationparameter\c!continue}%
388      \ifx\p_continue\v!yes \else
389        \normalexpanded{\strc_labels_reset{\v!combination\ifempty\currentcombination\else:\currentcombination\fi}{1}}%
390     \fi
391    \fi
392\to \everycombination
393
394\def\pack_combinations_pickup_caption
395  {\dostoptagged
396   \dostoptagged
397   \setbox\b_pack_combinations_caption\box\nextbox
398   \pack_combinations_pickup_package_pair}
399
400\def\pack_combinations_caption_first
401  {\futurelet\nexttoken\pack_combinations_caption_second}
402
403\def\pack_combinations_caption_second
404  {\ifx\nexttoken\egroup
405     % the caption is empty
406   \orelse\ifx\nexttoken\stopcaption
407     % the caption is empty (new per 2014-05-24)
408   \else
409     % todo: \p_pack_combinations_alternative\v!none: no style, strut etc
410     \hsize\wd\b_pack_combinations_content
411     \usealignparameter\combinationparameter
412     \usecombinationstyleandcolor\c!style\c!color
413     \bgroup
414     \aftergroup\endstrut
415     \aftergroup\egroup
416     \begstrut
417   \fi}
418
419\def\pack_combinations_pickup_package_pair % we need to store the caption row
420  {\vbox
421     {\forgetall
422      \m_pack_combinations_valigner{\box\b_pack_combinations_content}%
423      % we need to save the caption for a next alignment line
424      \pack_combinations_save_caption}%
425   \ifnum\c_pack_combinations_y>\plusone
426     \global\advance\c_pack_combinations_y\minusone
427     \global\advance\c_pack_combinations_x\minusone
428     \ifcase\c_pack_combinations_x
429       \doubleexpandafter\pack_combinations_pickup_package_pair_a
430     \else
431       \doubleexpandafter\pack_combinations_pickup_package_pair_b
432     \fi
433   \else
434     \singleexpandafter\pack_combinations_pickup_package_pair_c
435   \fi}
436
437\def\pack_combinations_pickup_package_pair_a
438  {\cr
439   \pack_combinations_flush_captions
440   \noalign
441     {\forgetall
442      \global\setbox\b_pack_combinations_captions\emptybox
443      \nointerlineskip
444      \combinationparameter\c!after
445      \combinationparameter\c!before
446      \vss
447      \nointerlineskip}%
448   \global\c_pack_combinations_x\c_pack_combinations_max
449   \pack_combinations_pickup}
450
451\def\pack_combinations_pickup_package_pair_b
452  {\aligntab
453   \aligntab
454   \aligntab
455   \kern\p_distance
456   \aligntab
457   \pack_combinations_pickup}
458
459\def\pack_combinations_pickup_package_pair_c
460  {\cr
461   \pack_combinations_flush_captions
462   \egroup}
463
464\installcorenamespace{combinationcaption}
465
466\def\pack_combinations_save_caption
467  {\ifdim\htdp\b_pack_combinations_caption>\d_pack_combinations_ht
468     \global\d_pack_combinations_ht\htdp\b_pack_combinations_caption
469   \fi
470   \savebox{\??combinationcaption:\number\c_pack_combinations_nesting}{\number\c_pack_combinations_x}{\box\b_pack_combinations_caption}}
471
472\let\pack_combinations_flush_captions_indeed\relax
473
474\def\pack_combinations_flush_captions
475  {\noalign
476     {\ifdim\d_pack_combinations_ht>\zeropoint
477        \nointerlineskip % indeed
478        \combinationparameter\c!inbetween
479        \global\c_pack_combinations_x\c_pack_combinations_max
480        \glet\pack_combinations_flush_captions_indeed\pack_combinations_flush_captions_yes
481      \else
482        \glet\pack_combinations_flush_captions_indeed\pack_combinations_flush_captions_nop
483      \fi}%
484   \pack_combinations_flush_captions_indeed
485   \crcr}
486
487\def\pack_combinations_flush_captions_yes
488  {\vpack to \d_pack_combinations_ht\bgroup
489     \foundbox{\??combinationcaption:\number\c_pack_combinations_nesting}{\number\c_pack_combinations_x}%
490     \vss
491   \egroup
492   \global\advance\c_pack_combinations_x\minusone
493   \ifnum\c_pack_combinations_x>\zerocount % \c_pack_combinations_max
494     \expandafter\pack_combinations_flush_captions_yes_followup
495   \else
496     \global\d_pack_combinations_ht\zeropoint
497     \initializeboxstack{\??combinationcaption:\number-\c_pack_combinations_nesting}%
498   \fi}
499
500\let\pack_combinations_flush_captions_nop\donothing
501
502\def\pack_combinations_flush_captions_yes_followup
503  {\aligntab
504   \aligntab
505   \aligntab
506   \aligntab
507   \pack_combinations_flush_captions_indeed}
508
509%D \macros
510%D   {startfloatcombination}
511%D
512%D \setupexternalfigures[directory={../sample}]
513%D \startbuffer
514%D \placefigure
515%D   [left,none]
516%D   {}
517%D   {\startfloatcombination[2*2]
518%D      \placefigure{alpha}{\externalfigure[cow.pdf][width=1cm]}
519%D      \placefigure{beta} {\externalfigure[cow.pdf][width=2cm]}
520%D      \placefigure{gamma}{\externalfigure[cow.pdf][width=3cm]}
521%D      \placefigure{delta}{\externalfigure[cow.pdf][width=4cm]}
522%D    \stopfloatcombination}
523%D
524%D \input tufte
525%D \stopbuffer
526%D
527%D \typebuffer \getbuffer
528
529\protected\def\pack_combinations_float_hack_a#1%
530  {\strc_floats_build_box_separate_split{#1}%
531   \box\b_strc_floats_separate_content}
532
533\protected\def\pack_combinations_float_hack_b#1%
534  {\box\b_strc_floats_separate_caption}
535
536\permanent\tolerant\protected\def\startfloatcombination[#1]#*[#2]%
537  {\ifinsidefloat\else\dontleavehmode\fi % tricky, floatcombinations fail to align well otherwise
538   \vbox\bgroup
539   \strc_floats_build_box_separate_set
540  %\insidecolumnstrue % trick, forces no centering, todo: proper switch/feature
541   \postcenterfloatmethod\zerocount
542   \forcelocalfloats
543   \enforced\permanent\protected\def\stopfloatcombination{\pack_combinations_stop_float{#1}}}
544
545\aliased\let\stopfloatcombination\relax
546
547\def\pack_combinations_float_check_x_y
548  {\ifnum\numexpr\c_pack_combinations_x*\c_pack_combinations_y\relax<\noflocalfloats\relax
549       \global\c_pack_combinations_x\noflocalfloats
550       \global\c_pack_combinations_y\plusone
551   \fi
552   \let\pack_combinations_check_x_y\relax}%
553
554\def\pack_combinations_stop_float#1%
555  {\scratchtoks\emptytoks
556   \dorecurse\noflocalfloats
557     {\appendetoks
558        {\pack_combinations_float_hack_a{\recurselevel}}%
559        {\pack_combinations_float_hack_b{\recurselevel}}%
560      \to\scratchtoks}% brrr
561   \let\pack_combinations_check_x_y\pack_combinations_float_check_x_y
562   \expanded{\startcombination[#1]\the\scratchtoks}\stopcombination
563   \resetlocalfloats
564   \egroup}
565
566%D \macros
567%D   {definepairedbox, setuppairedbox, placepairedbox}
568%D
569%D Paired boxes, formally called legends, but from now on a legend is just an
570%D instance, are primarily meant for typesetting some text alongside an
571%D illustration. Although there is quite some variation possible, the functionality
572%D is kept simple, if only because in most cases such pairs are typeset sober.
573%D
574%D The location specification accepts a pair, where the first keyword specifies the
575%D arrangement, and the second one the alignment. The first key of the location pair
576%D is one of \type {left}, \type {right}, \type {top} or \type {bottom}, while the
577%D second key can also be \type {middle}.
578%D
579%D The first box is just collected in an horizontal box, but the second one is a
580%D vertical box that gets passed the bodyfont and alignment settings.
581%D
582%D In many cases the table builders can be used instead, but as this mechanism is a
583%D traditional \CONTEXT\ one we keep it around.
584
585%D \macros
586%D   {setuplegend, placelegend}
587%D
588%D It makes sense to typeset a legend to a figure in \TEX\ and not in a drawing
589%D package. The macro \type {\placelegend} combines a figure (or something else) and
590%D its legend. This command is just a paired box.
591%D
592%D The legend is placed according to \type {location}, being \type {bottom} or \type
593%D {right}. The macro macro is used as follows.
594%D
595%D \starttyping
596%D \placefigure
597%D   {whow}
598%D   {\placelegend
599%D      {\externalfigure[cow]}
600%D      {\starttabulate
601%D       \NC 1 \NC head \NC \NR
602%D       \NC 2 \NC legs \NC \NR
603%D       \NC 3 \NC tail \NC \NR
604%D       \stoptabulate}}
605%D
606%D \placefigure
607%D   {whow}
608%D   {\placelegend
609%D      {\externalfigure[cow]}
610%D      {\starttabulate[|l|l|l|l|]
611%D       \NC 1 \NC head \NC 3 \NC tail \NC \NR
612%D       \NC 2 \NC legs \NC   \NC      \NC \NR
613%D       \stoptabulate}}
614%D
615%D \placefigure
616%D   {whow}
617%D   {\placelegend[n=2]
618%D      {\externalfigure[cow]}
619%D      {\starttabulate
620%D       \NC 1 \NC head \NC \NR
621%D       \NC 2 \NC legs \NC \NR
622%D       \NC 3 \NC tail \NC \NR
623%D       \stoptabulate}}
624%D
625%D \placefigure
626%D   {whow}
627%D   {\placelegend[n=2]
628%D      {\externalfigure[cow]}
629%D      {head \par legs \par tail}}
630%D
631%D \placefigure
632%D   {whow}
633%D   {\placelegend[n=2]
634%D      {\externalfigure[cow]}
635%D      {\startitemize[packed]
636%D       \item head \item legs \item  tail \item belly \item horns
637%D       \stopitemize}}
638%D
639%D \placefigure
640%D   {whow}
641%D   {\placelegend[n=2,width=.8\hsize]
642%D      {\externalfigure[cow]}
643%D      {\startitemize[packed]
644%D       \item head \item legs \item  tail \item belly \item horns
645%D       \stopitemize}}
646%D
647%D \def\MytTestTwo#1#2%
648%D   {\placefigure
649%D      {whow}
650%D      {\placelegend[location={#1,#2}]
651%D         {\externalfigure[cow]}
652%D         {\starttabulate
653%D          \NC 1 \NC head \NC \NR
654%D          \NC 2 \NC legs \NC \NR
655%D          \NC 3 \NC tail \NC \NR
656%D          \stoptabulate}}}
657%D
658%D \def\MytTestOne#1{\processcommalist[left,right,top,bottom]{\MytTestTwo{#1}}}
659%D
660%D \processcommalist[left,right,top,bottom,middle]\MytTestOne
661%D \stoptyping
662%D
663%D More structure is also possible (the order matters!):
664%D
665%D \starttyping
666%D \startplacefigure[title=whow]
667%D    \startplacelegend[location={bottom,middle},color=red]
668%D         \startcontent
669%D             \externalfigure[cow]
670%D         \stopcontent
671%D         \startcaption
672%D             \starttabulate[|l|l|]
673%D                 \NC 1 \NC head \NC \NR
674%D                 \NC 2 \NC legs \NC \NR
675%D                 \NC 3 \NC tail \NC \NR
676%D             \stoptabulate
677%D         \stopcaption
678%D    \stopplacelegend
679%D \stopplacefigure
680%D \stoptyping
681
682% todo: natural size
683
684\newsystemmode{pairedbox}
685
686\appendtoks
687    \globalresetsystemmode{pairedbox}%
688\to \everyinsidefloat
689
690\installcorenamespace {pairedbox}
691
692\installcommandhandler \??pairedbox {pairedbox} \??pairedbox
693
694\setuppairedbox
695 [\c!n=1,
696  \c!distance=\bodyfontsize,
697 %\c!before=,
698 %\c!after=,
699 %\c!color=,
700 %\c!style=,
701  \c!inbetween={\blank[\v!medium]},
702  \c!width=\hsize,
703  \c!height=\vsize,
704  \c!maxwidth=\textwidth,   % \makeupwidth,
705  \c!maxheight=\textheight, % \makeupheight,
706 %\c!bodyfont=,
707 %\c!align=,
708  \c!location=\v!bottom]
709
710% watch the hsize/vsize tricks
711
712\newbox  \b_pack_pairedboxes_first
713\newbox  \b_pack_pairedboxes_second
714\newdimen\s_pack_pairedboxes_size
715
716\appendtoks
717   \frozen\instance\setuevalue{\e!setup\currentpairedbox\e!endsetup}{\setuppairedbox     [\currentpairedbox]}%
718   \frozen\instance\setuevalue{\e!place\currentpairedbox           }{\placepairedbox     [\currentpairedbox]}% one argument is mandate anyway
719   \frozen\instance\setuevalue{\e!start\e!place\currentpairedbox   }{\startplacepairedbox[\currentpairedbox]}% one argument is mandate anyway
720   \frozen\instance\setuevalue{\e!stop\e!place \currentpairedbox   }{\stopplacepairedbox                    }%
721\to \everydefinepairedbox
722
723\permanent\tolerant\protected\def\startplacepairedbox[#1]#*[#2]%
724  {\bgroup
725   \edef\currentpairedbox{#1}%
726   \setupcurrentpairedbox[#2]%
727   \pairedboxparameter\c!before
728   \bgroup
729   \edef\p_location{\pairedboxparameter\c!location}%
730   \edef\p_n       {\pairedboxparameter\c!n}%
731   %
732   \enforced\let\startcontent\pack_common_content_start
733   \enforced\let\stopcontent \pack_common_content_stop
734   \enforced\let\startcaption\pack_common_caption_start
735   \enforced\let\stopcaption \pack_common_caption_stop
736   %
737   \globalsetsystemmode{pairedbox}%
738   \pack_pairedboxes_before
739   \expandafterpars\pack_pairedboxes_first_pickup}
740
741\permanent\protected\def\stopplacepairedbox{} % we just pick up two boxes
742
743\aliased\let\placepairedbox\startplacepairedbox    % we just pick up two boxes
744
745\def\pack_pairedboxes_first_pickup
746  {\dowithnextboxcs\pack_pairedboxes_first\hbox
747     \bgroup
748     \let\next=}
749
750\def\pack_pairedboxes_first
751  {\pack_pairedboxes_between
752   \expandafterpars\pack_pairedboxes_second_pickup}
753
754\def\pack_pairedboxes_second_pickup
755  {\dowithnextboxcs\pack_pairedboxes_second\vbox
756     \bgroup
757     \pack_pairedboxes_inside_second
758     \let\next=}
759
760\def\pack_pairedboxes_second
761  {\pack_pairedboxes_after
762   \egroup
763   \pairedboxparameter\c!after
764   \egroup}
765
766\newconditional\c_pack_pairedboxes_horizontal \settrue\c_pack_pairedboxes_horizontal
767
768\installcorenamespace{pairedboxnature}
769\installcorenamespace{pairedboxalign}
770
771\let\pack_pairedboxes_flush      \relax
772\let\pack_pairedboxes_fill_top   \relax
773\let\pack_pairedboxes_fill_bottom\relax
774
775\defcsname\??pairedboxnature\v!left\endcsname
776  {\settrue\c_pack_pairedboxes_horizontal
777   \let\pack_pairedboxes_flush\pack_pairedboxes_flush_left}
778
779\defcsname\??pairedboxnature\v!right\endcsname
780  {\settrue\c_pack_pairedboxes_horizontal
781   \let\pack_pairedboxes_flush\pack_pairedboxes_flush_right}
782
783\defcsname\??pairedboxnature\v!top\endcsname
784  {\setfalse\c_pack_pairedboxes_horizontal
785   \let\pack_pairedboxes_fill_top\relax
786   \let\pack_pairedboxes_fill_bottom\vss
787   \let\pack_pairedboxes_flush\pack_pairedboxes_flush_top}
788
789\defcsname\??pairedboxnature\v!bottom\endcsname
790  {\setfalse\c_pack_pairedboxes_horizontal
791   \let\pack_pairedboxes_fill_top\vss
792   \let\pack_pairedboxes_fill_bottom\relax
793   \let\pack_pairedboxes_flush\pack_pairedboxes_flush_bottom}
794
795\def\pack_pairedboxes_flush_left
796  {\box\b_pack_pairedboxes_second
797   \kern\pairedboxparameter\c!distance
798   \box\b_pack_pairedboxes_first}
799
800\def\pack_pairedboxes_flush_right
801  {\box\b_pack_pairedboxes_first
802   \kern\pairedboxparameter\c!distance
803   \box\b_pack_pairedboxes_second}
804
805\def\pack_pairedboxes_flush_top
806  {\box\b_pack_pairedboxes_second
807   \endgraf
808   \nointerlineskip
809   \pairedboxparameter\c!inbetween
810   \box\b_pack_pairedboxes_first}
811
812\def\pack_pairedboxes_flush_bottom
813  {\box\b_pack_pairedboxes_first
814   \endgraf
815   \nointerlineskip
816   \pairedboxparameter\c!inbetween
817   \box\b_pack_pairedboxes_second}
818
819\let\pack_pairedboxes_align_l\relax
820\let\pack_pairedboxes_align_r\relax
821\let\pack_pairedboxes_align_t\relax
822\let\pack_pairedboxes_align_b\relax
823
824\defcsname\??pairedboxalign\v!left\endcsname % 0
825  {\let\pack_pairedboxes_align_l\relax
826   \let\pack_pairedboxes_align_r\hss
827   \let\pack_pairedboxes_align_t\relax
828   \let\pack_pairedboxes_align_b\relax}
829
830\defcsname\??pairedboxalign\v!right\endcsname % 1
831  {\let\pack_pairedboxes_align_l\hss
832   \let\pack_pairedboxes_align_r\relax
833   \let\pack_pairedboxes_align_t\relax
834   \let\pack_pairedboxes_align_b\relax}
835
836\defcsname\??pairedboxalign\v!high\endcsname % 2
837  {\let\pack_pairedboxes_align_l\relax
838   \let\pack_pairedboxes_align_r\relax
839   \let\pack_pairedboxes_align_t\relax
840   \let\pack_pairedboxes_align_b\vss}
841
842\defcsname\??pairedboxalign\v!low\endcsname % 3
843  {\let\pack_pairedboxes_align_l\relax
844   \let\pack_pairedboxes_align_r\relax
845   \let\pack_pairedboxes_align_t\vss
846   \let\pack_pairedboxes_align_b\relax}
847
848\defcsname\??pairedboxalign\v!middle\endcsname % 4
849  {\let\pack_pairedboxes_align_l\hss
850   \let\pack_pairedboxes_align_r\hss
851   \let\pack_pairedboxes_align_t\vss
852   \let\pack_pairedboxes_align_b\vss}
853
854\defcsname\??pairedboxalign\v!bottom\endcsname{\csname\??pairedboxalign\v!low \endcsname}
855\defcsname\??pairedboxalign   \v!top\endcsname{\csname\??pairedboxalign\v!high\endcsname}
856
857\def\pack_pairedbox_valign#1{\setbox#1\vpack to \s_pack_pairedboxes_size{\pack_pairedboxes_align_t\box#1\pack_pairedboxes_align_b}}
858\def\pack_pairedbox_halign#1{\setbox#1\hpack to \s_pack_pairedboxes_size{\pack_pairedboxes_align_l\box#1\pack_pairedboxes_align_r}}
859
860\def\pack_pairedboxes_before
861  {\ifempty\p_location
862      \csname\??pairedboxnature\v!left  \endcsname
863      \csname\??pairedboxalign \v!middle\endcsname
864   \else
865     \getfromcommacommand[\p_location][1]%
866     \csname\??pairedboxnature
867       \ifcsname\??pairedboxnature\commalistelement\endcsname\commalistelement\else\v!left\fi
868     \endcsname
869     \getfromcommacommand[\p_location][2]%
870     \csname\??pairedboxalign
871       \ifcsname\??pairedboxalign\commalistelement\endcsname\commalistelement\else\v!middle\fi
872     \endcsname
873   \fi}
874
875\def\pack_pairedboxes_between
876  {\usebodyfontparameter\pairedboxparameter
877   \setbox\b_pack_pairedboxes_first\box\nextbox
878   \ifconditional\c_pack_pairedboxes_horizontal
879     \pack_pairedboxes_between_horizontal
880   \else
881     \pack_pairedboxes_between_vertical
882   \fi
883   \ifnum\p_n>\plusone
884     \setrigidcolumnhsize\hsize{\pairedboxparameter\c!distance}\p_n
885   \fi}
886
887\def\pack_pairedboxes_between_horizontal
888  {\scratchdistance\pairedboxparameter\c!distance
889   \scratchwidth\pairedboxparameter\c!maxwidth\relax
890   \setlocalhsize
891   \hsize\dimexpr\availablehsize-\wd\b_pack_pairedboxes_first-\scratchdistance\relax
892   \hsize\pairedboxparameter\c!width\relax % can be \hsize
893   \scratchdimen\dimexpr\wd\b_pack_pairedboxes_first+\scratchdistance\relax
894   \ifdim\dimexpr\hsize+\scratchdimen\relax>\scratchwidth
895     \hsize\dimexpr\scratchwidth-\scratchdimen\relax
896   \fi}
897
898\def\pack_pairedboxes_between_vertical
899  {\scratchwidth\pairedboxparameter\c!maxwidth\relax
900   \hsize\wd\b_pack_pairedboxes_first
901   \hsize\pairedboxparameter\c!width\relax % can be \hsize
902   \ifdim\hsize>\scratchwidth\relax
903     \hsize\scratchwidth
904   \fi}
905
906\def\pack_pairedboxes_after
907  {\setbox\b_pack_pairedboxes_second\vpack
908     {\ifnum\p_n>\plusone
909        \rigidcolumnbalance\nextbox
910      \else
911        \box\nextbox
912      \fi}%
913   \ifconditional\c_pack_pairedboxes_horizontal
914     \pack_pairedboxes_pack_horizontal
915   \else
916     \pack_pairedboxes_pack_vertical
917   \fi}
918
919\def\pack_pairedboxes_pack_horizontal
920  {\dontleavehmode\hbox\bgroup
921     \forgetall
922     \s_pack_pairedboxes_size\ht
923       \ifdim\ht\b_pack_pairedboxes_first>\ht\b_pack_pairedboxes_second
924         \b_pack_pairedboxes_first
925       \else
926         \b_pack_pairedboxes_second
927       \fi
928     \vsize\s_pack_pairedboxes_size
929     \ifdim\s_pack_pairedboxes_size<\pairedboxparameter\c!height\relax % can be \vsize
930       \s_pack_pairedboxes_size\pairedboxparameter\c!height
931     \fi
932     \ifdim\s_pack_pairedboxes_size>\pairedboxparameter\c!maxheight\relax
933       \s_pack_pairedboxes_size\pairedboxparameter\c!maxheight
934     \fi
935     \pack_pairedbox_valign\b_pack_pairedboxes_first
936     \pack_pairedbox_valign\b_pack_pairedboxes_second
937     \pack_pairedboxes_flush
938   \egroup}
939
940\def\pack_pairedboxes_pack_vertical
941  {\dontleavehmode\vpack\bgroup
942     \forgetall
943     \s_pack_pairedboxes_size\wd
944       \ifdim\wd\b_pack_pairedboxes_first>\wd\b_pack_pairedboxes_second
945         \b_pack_pairedboxes_first
946       \else
947         \b_pack_pairedboxes_second
948       \fi
949     \pack_pairedbox_halign\b_pack_pairedboxes_first
950     \pack_pairedbox_halign\b_pack_pairedboxes_second
951     \s_pack_pairedboxes_size\ht\b_pack_pairedboxes_second
952     \vsize\s_pack_pairedboxes_size
953     \ifdim\ht\b_pack_pairedboxes_second<\pairedboxparameter\c!height\relax % can be \vsize
954       \s_pack_pairedboxes_size\pairedboxparameter\c!height\relax % \relax needed
955     \fi
956     \ifdim\s_pack_pairedboxes_size>\pairedboxparameter\c!maxheight\relax % todo: totale hoogte
957       \s_pack_pairedboxes_size\pairedboxparameter\c!maxheight\relax % \relax needed
958     \fi
959     \ifdim\s_pack_pairedboxes_size>\ht\b_pack_pairedboxes_second
960       \setbox\b_pack_pairedboxes_second\vpack to \s_pack_pairedboxes_size
961         {\pack_pairedboxes_fill_top
962          \box\b_pack_pairedboxes_second
963          \pack_pairedboxes_fill_bottom}% \kern\zeropoint
964     \fi
965     \pack_pairedboxes_flush
966   \egroup}
967
968\def\pack_pairedboxes_inside_second
969  {\forgetall
970   \setupalign[\pairedboxparameter\c!align]%
971   \usepairedboxstyleandcolor\c!style\c!color
972   \tolerantTABLEbreaktrue % hm.
973   \blank[\v!disable]% use fast one
974   \everypar{\begstrut}} % also flushers here (see bTABLE)
975
976\definepairedbox[\v!legend]
977
978\permanent\protected\def\placeontopofeachother{\bgroup\dowithnextboxcs\pack_topofeachother_one\hbox}
979\permanent\protected\def\placesidebyside      {\bgroup\dowithnextboxcs\pack_sidebyside_one    \hbox}
980
981\def\pack_topofeachother_one{\bgroup\setbox\scratchboxone\box\nextbox\dowithnextboxcs\pack_topofeachother_two\hbox}
982\def\pack_sidebyside_one    {\bgroup\setbox\scratchboxone\box\nextbox\dowithnextboxcs\pack_sidebyside_two    \hbox}
983
984\def\pack_topofeachother_two{\setbox\scratchboxtwo\box\nextbox
985                             \halign{\hss\aligncontent\hss\cr\box\scratchboxone\cr\box\scratchboxtwo\cr}%
986                             \egroup\egroup}
987\def\pack_sidebyside_two    {\setbox\scratchboxtwo\box\nextbox
988                             \valign{\vss\aligncontent\vss\cr\box\scratchboxone\cr\box\scratchboxtwo\cr}%
989                             \egroup\egroup}
990
991\protect \endinput
992