summaryrefslogtreecommitdiff
path: root/tex/context/base/supp-box.mkiv
diff options
context:
space:
mode:
Diffstat (limited to 'tex/context/base/supp-box.mkiv')
-rw-r--r--tex/context/base/supp-box.mkiv3126
1 files changed, 3126 insertions, 0 deletions
diff --git a/tex/context/base/supp-box.mkiv b/tex/context/base/supp-box.mkiv
new file mode 100644
index 000000000..f1ed751a0
--- /dev/null
+++ b/tex/context/base/supp-box.mkiv
@@ -0,0 +1,3126 @@
+%D \module
+%D [ file=supp-box,
+%D version=1995.10.10,
+%D title=\CONTEXT\ Support Macros,
+%D subtitle=Boxes,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA / Hans Hagen \& Ton Otten}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D This module implements some box manipulation macros. Some
+%D are quite simple, some are more advanced and when understood
+%D well, all can be of use.
+
+%D No longer generic, why bother.
+
+\writestatus{loading}{ConTeXt Support Macros / Boxes}
+
+\unprotect
+
+%D \macros
+%D {strutdp,strutht,strutwd}
+%D
+%D The next shortcuts save memory and keying. The width is
+%D normally zero points (if not, you're in trouble). These
+%D shortcuts can be used like a dimension, opposite to the
+%D core macros \type {\strutdepth} and alike, which are
+%D values.
+
+\def\strutdp{\dp\strutbox}
+\def\strutht{\ht\strutbox}
+\def\strutwd{\wd\strutbox}
+
+%D \macros
+%D {resetbox, emptybox}
+%D
+%D Let's start with an easy one. The next macro hides the
+%D ugly \type {@} in \type {\voidb@x}.
+
+\ifx\voidbox\undefined \newbox\voidbox \fi
+\ifx\voidb@x\undefined \let\voidb@x\voidbox \fi
+
+\def\emptybox {\box \voidbox}
+\def\unvoidbox {\unhbox\voidbox}
+\def\resetbox#1{\setbox#1\box\voidbox}
+
+%D \macros
+%D {nextdepth}
+%D
+%D Let's start with a rather simple declaration. Sometimes we
+%D need to save the \TEX\ \DIMENSION\ \type{\prevdepth} and
+%D append it later on. The name \type{\nextdepth} suits
+%D this purpose well.
+
+\newdimen\nextdepth
+
+%D \macros
+%D {smashbox, smashedbox}
+%D
+%D Smashing is introduced in \PLAIN\ \TEX, and stands for
+%D reducing the dimensions of a box to zero. The most resolute
+%D one is presented first.
+
+\def\smashbox#1%
+ {\wd#1\zeropoint
+ \ht#1\zeropoint
+ \dp#1\zeropoint}
+
+\def\smashboxed#1%
+ {\smashbox{#1}%
+ \box#1\relax}
+
+%D \macros
+%D {hsmashbox,vsmashbox}
+%D
+%D Smashing can be used for overlaying boxes. Depending on
+%D the mode, horizontal or vertical, one can use:
+
+\def\hsmashbox#1%
+ {\wd#1\zeropoint}
+
+\def\vsmashbox#1%
+ {\ht#1\zeropoint
+ \dp#1\zeropoint}
+
+%D The next implementation is less sensitive for spurious
+%D spaces.
+
+\newcount\registercount
+
+\def\smashbox
+ {\afterassignment\dosmashbox\registercount}
+
+\def\dosmashbox
+ {\wd\registercount\zeropoint
+ \ht\registercount\zeropoint
+ \dp\registercount\zeropoint}
+
+\def\smashedbox
+ {\afterassignment\thesmashedbox\registercount}
+
+\def\thesmashedbox
+ {\dosmashbox
+ \box\registercount}
+
+\def\hsmashbox
+ {\afterassignment\dohsmashbox\registercount}
+
+\def\dohsmashbox
+ {\wd\registercount\zeropoint}
+
+\def\vsmashbox
+ {\afterassignment\dovsmashbox\registercount}
+
+\def\dovsmashbox
+ {\ht\registercount\zeropoint
+ \dp\registercount\zeropoint}
+
+%D \macros
+%D {hsmash,vsmash,
+%D hsmashed,vsmashed}
+%D
+%D While the previous macros expected a \BOX, the next act on a
+%D content. They are some subtle differences betreen the smash
+%D and smashed alternatives. The later ones reduce all
+%D dimensions to zero.
+
+% Ok, but inefficient and/or catcode unsafe:
+%
+% \def\hsmash #1{\bgroup\setbox0=\normalhbox{#1}\hsmashbox0\box0\egroup}
+% \def\vsmash #1{\bgroup\setbox0=\normalvbox{#1}\vsmashbox0\box0\egroup}
+% \def\hsmashed#1{\bgroup\setbox0=\normalhbox{#1}\smashbox 0\box0\egroup}
+% \def\vsmashed#1{\bgroup\setbox0=\normalvbox{#1}\smashbox 0\box0\egroup}
+%
+% Better, but a waste of tokens:
+%
+% \def\hsmash {\bgroup\dowithnextbox{\hsmashbox\nextbox\flushnextbox\egroup}\normalhbox}
+% \def\vsmash {\bgroup\dowithnextbox{\vsmashbox\nextbox\flushnextbox\nextbox\egroup}\normalvbox}
+% \def\hsmashed{\bgroup\dowithnextbox{\smashbox \nextbox\flushnextbox\nextbox\egroup}\normalhbox}
+% \def\vsmashed{\bgroup\dowithnextbox{\smashbox \nextbox\flushnextbox\nextbox\egroup}\normalvbox}
+%
+% The best:
+
+\def\dosomesmash#1% (begin|end)group ipv (b|e)group ?
+ {\bgroup\dowithnextbox{#1\nextbox\flushnextbox\egroup}}
+
+\def\hsmash {\dosomesmash\hsmashbox\normalhbox}
+\def\vsmash {\dosomesmash\vsmashbox\normalvbox}
+\def\hsmashed{\dosomesmash\smashbox \normalhbox}
+\def\vsmashed{\dosomesmash\smashbox \normalvbox}
+
+%D \macros
+%D {smashedhbox,smashedvbox}
+%D
+%D Also handy (all dimensions zeroed):
+%D
+%D \starttyping
+%D \smashedhbox to ... {...}
+%D \smashedvbox to ... {...}
+%D \stoptyping
+
+\def\dosmashedbox#1%
+ %{#1\bgroup\dowithnextbox{\smashbox\nextbox\flushnextbox\egroup}#1}
+ {#1\bgroup\dowithnextbox{\smashedbox\nextbox\egroup}#1}
+
+\def\smashedhbox{\dosmashedbox\hbox}
+\def\smashedvbox{\dosmashedbox\vbox}
+
+%D \macros
+%D {smash}
+%D
+%D This smash alternative takes an optional arg [whdtb] as
+%D well as is potentially catcode safer. It is needed by the
+%D math module (although the \type {\leavevmode} is not added
+%D here).
+
+\def\smash
+ {\futurelet\nexttoken\dosmash}
+
+\def\dosmash
+ {\ifx\nexttoken[\@EA\dodosmash\else\@EA\donosmash\fi}
+
+\def\donosmash
+ {\dodosmash[hd]}
+
+\def\dodosmash[#1]%
+ {\edef\@@smash{#1}\futurelet\nexttoken\dododosmash}
+
+\def\dododosmash % if needed we can avoid the \next
+ {\ifmmode
+ \def\next##1{\mathpalette\mathsm@sh{##1}}%
+ \else\ifx\nexttoken\bgroup
+ \let\next\finsm@sh
+ \else
+ \def\next##1{\finsm@sh{##1}}%
+ \fi\fi
+ \next}
+
+\def\mathsm@sh#1#2% redefined plain macro
+ {\finsm@sh{$\mathsurround\zeropoint#1{#2}$}}
+
+\def\makesm@sh#1% redefined plain macro (handles t b h d w)
+ {\if#1w\nextboxwd\zeropoint\else
+ \if#1h\nextboxht\zeropoint\else
+ \if#1d\nextboxdp\zeropoint\else
+ \if#1t\nextboxht\zeropoint\else
+ \if#1b\nextboxdp\zeropoint\fi\fi\fi\fi\fi}
+
+\def\finsm@sh % redefined plain macro
+ {\dowithnextbox{\@EA\handletokens\@@smash\with\makesm@sh\flushnextbox}\normalhbox}
+
+%D \starttabulate[|l|l|]
+%D \NC w \NC \ruledhbox{\smash [w]{This is some great smashing, isn't it?}} \NC \NR
+%D \NC h \NC \ruledhbox{\smash [h]{This is some great smashing, isn't it?}} \NC \NR
+%D \NC d \NC \ruledhbox{\smash [d]{This is some great smashing, isn't it?}} \NC \NR
+%D \NC tb \NC \ruledhbox{\smash [tb]{This is some great smashing, isn't it?}} \NC \NR
+%D \NC whd \NC \ruledhbox{\smash[whd]{This is some great smashing, isn't it?}} \NC \NR
+%D \stoptabulate
+
+%D \macros
+%D {phantom, hphantom, vphantom, mathstrut}
+%D
+%D The next implementation of \type {\phantom} cum suis does
+%D not grab an argument in the non||math case, which is better.
+
+\unexpanded\def\phantom {\ph@nt\nextbox\nextbox\nextbox}
+\unexpanded\def\vphantom{\ph@nt\nextbox\nextbox\voidbox}
+\unexpanded\def\hphantom{\ph@nt\voidbox\voidbox\nextbox}
+
+%D Due to a complicated call to \type {\mathpallete} and
+%D thereby \type {\mathchoice}, the next macro looks ugly.
+%D We also take care of non||braced arguments.
+
+\def\ph@nt#1#2#3%
+ {\def\doph@nt
+ {\ifmmode
+ \def\mathph@nt####1####2{\makeph@nt#1#2#3{$\mathsurround\zeropoint####1{####2}$}}%
+ \def\nextph@nt{\mathpalette\mathph@nt}%
+ \else\ifx\nextph@nt\bgroup
+ \def\nextph@nt{\makeph@nt#1#2#3}%
+ \else
+ \def\nextph@nt####1{\makeph@nt#1#2#3{####1}}%
+ \fi\fi
+ \nextph@nt}%
+ \futurelet\nextph@nt\doph@nt}
+
+\def\makeph@nt#1#2#3%
+ {\begingroup
+ \dowithnextbox
+ {\setbox\scratchbox\null
+ \ht\scratchbox\ht#1%
+ \dp\scratchbox\dp#2%
+ \wd\scratchbox\wd#3%
+ \box\scratchbox
+ \endgroup}
+ \normalhbox}
+
+\let\finph@nt\undefined
+
+%D We also define plain's \type {\mathstrut}.
+
+\unexpanded\def\mathstrut{\vphantom{(}}
+
+%D \macros
+%D {getboxheight}
+%D
+%D Although often needed, \TEX\ does not support arithmics
+%D like:
+%D
+%D \starttyping
+%D \dimen0 = \ht0 + \dp0
+%D \stoptyping
+%D
+%D so we implemented:
+%D
+%D \starttyping
+%D \getboxheight ... \of \box...
+%D \stoptyping
+%D
+%D For instance,
+%D
+%D \starttyping
+%D \getboxheight \dimen0 \of \box0
+%D \getboxheight \someheight \of \box \tempbox
+%D \stoptyping
+%D
+%D The implementation is rather stupid:
+%D
+%D \starttyping
+%D \def\getboxheight#1\of#2\box#3%
+%D {#1\ht#3\advance#1\dp#3\relax}
+%D \stoptyping
+%D
+%D The next alternative is slightly more clever, since
+%D it accepts \type {{12}} as well as \type {12} as box
+%D number.
+
+\def\getboxheight#1\of#2\box#3%
+ {\def\next{#1\dimexpr\ht\registercount+\dp\registercount\relax}%
+ \afterassignment\next\registercount=#3}
+
+%D For a long time the following three macros were part of
+%D the grid snapping core module, but it makes more sense to
+%D have them here so that users can see them.
+%D
+%D \macros
+%D {getnoflines, getroundednoflines, getrawnoflines}
+%D
+%D Het commando \type{\getnoflines} converteert een hoogte
+%D (dimensie) in een aantal regels en kent dit toe aan
+%D \type{\noflines}.
+%D
+%D \starttyping
+%D \getnoflines{dimensie}
+%D \stoptyping
+%D
+%D Er wordt gedeeld door \type{\openlineheight} en een hoogte
+%D van~0pt komt overeen met 0~regels. The raw alternative
+%D does not round.
+
+%D For a long time we had:
+%D
+%D \starttyping
+%D \newcount\noflines
+%D \newdimen\noflinesheight
+%D
+%D \def\dogetnoflines#1#2%
+%D {\noflinesheight#2\relax
+%D \ifzeropt\noflinesheight % \ifdim\noflinesheight=\zeropoint
+%D \noflines\zerocount
+%D \else
+%D \divide\noflinesheight \openlineheight
+%D \noflines\noflinesheight
+%D #1\ifdim\noflines\openlineheight=#2\relax \else
+%D \advance\noflines\ifdim#2>\zeropoint\plusone\else\minusone\fi
+%D \fi\fi
+%D \fi}
+%D
+%D \def\getnoflines {\dogetnoflines\iftrue } % compensated
+%D \def\getrawnoflines{\dogetnoflines\iffalse} % no compensation
+%D \stoptyping
+%D
+%D A more recent variant is:
+
+\ifx\roundingeps\undefined \newdimen\roundingeps \roundingeps=10sp \fi
+
+\newcount\noflines
+\newdimen\noflinesheight
+
+% \def\getnoflines {\xdogetnoflines\plusone } % compensated
+% \def\getroundednoflines{\xdogetnoflines\plustwo } % rounded
+% \def\getrawnoflines {\xdogetnoflines\plusthree} % truncated
+%
+% \def\xdogetnoflines#1#2%
+% {\noflinesheight#2\relax
+% \ifzeropt\noflinesheight
+% \noflines\zerocount
+% \else\ifdim\noflinesheight>\zeropoint
+% \ifcase#1\or
+% \advance\noflinesheight-\roundingeps
+% \divide\noflinesheight\openlineheight
+% \noflines\noflinesheight
+% \advance\noflines\plusone
+% \or
+% \advance\noflinesheight\roundingeps
+% \divide\noflinesheight\openlineheight
+% \noflines\noflinesheight
+% \or
+% \advance\noflinesheight\roundingeps
+% \advance\noflinesheight.5\openlineheight
+% \divide\noflinesheight\openlineheight
+% \noflines\noflinesheight
+% \fi
+% \else
+% \ifcase#1\or
+% \advance\noflinesheight\roundingeps
+% \divide\noflinesheight\openlineheight
+% \noflines\noflinesheight
+% \advance\noflines\minusone
+% \or
+% \advance\noflinesheight-\roundingeps
+% \divide\noflinesheight\openlineheight
+% \noflines\noflinesheight
+% \or
+% \advance\noflinesheight-\roundingeps
+% \advance\noflinesheight-.5\openlineheight
+% \divide\noflinesheight\openlineheight
+% \noflines\noflinesheight
+% \fi
+% \fi\fi}
+
+\def\getnoflines#1%
+ {\noflinesheight#1\relax
+ \ifzeropt\noflinesheight
+ \noflines\zerocount
+ \else\ifdim\noflinesheight>\zeropoint
+ \advance\noflinesheight-\roundingeps
+ \divide\noflinesheight\openlineheight
+ \noflines\noflinesheight
+ \advance\noflines\plusone
+ \else
+ \advance\noflinesheight\roundingeps
+ \divide\noflinesheight\openlineheight
+ \noflines\noflinesheight
+ \advance\noflines\minusone
+ \fi\fi}
+
+\def\getroundednoflines#1%
+ {\noflinesheight#1\relax
+ \ifzeropt\noflinesheight
+ \noflines\zerocount
+ \else\ifdim\noflinesheight>\zeropoint
+ \advance\noflinesheight\roundingeps
+ \divide\noflinesheight\openlineheight
+ \noflines\noflinesheight
+ \else
+ \advance\noflinesheight-\roundingeps
+ \divide\noflinesheight\openlineheight
+ \noflines\noflinesheight
+ \fi\fi}
+
+\def\getrawnoflines#1%
+ {\noflinesheight#1\relax
+ \ifzeropt\noflinesheight
+ \noflines\zerocount
+ \else\ifdim\noflinesheight>\zeropoint
+ \advance\noflinesheight\roundingeps
+ \advance\noflinesheight.5\openlineheight
+ \divide\noflinesheight\openlineheight
+ \noflines\noflinesheight
+ \else
+ \advance\noflinesheight-\roundingeps
+ \advance\noflinesheight-.5\openlineheight
+ \divide\noflinesheight\openlineheight
+ \noflines\noflinesheight
+ \fi\fi}
+
+%D Let's proof that it works:
+%D
+%D \startbuffer
+%D \scratchdimen\dimexpr(3pt) \getnoflines\scratchdimen 1=\the\noflines \endgraf
+%D \scratchdimen\dimexpr(10\lineheight) \getnoflines\scratchdimen 10=\the\noflines \endgraf
+%D \scratchdimen\dimexpr(10.1\lineheight) \getnoflines\scratchdimen 11=\the\noflines \endgraf
+%D \scratchdimen\dimexpr(10.5\lineheight) \getnoflines\scratchdimen 11=\the\noflines \endgraf
+%D \scratchdimen\dimexpr(10.9\lineheight) \getnoflines\scratchdimen 11=\the\noflines \endgraf
+%D \scratchdimen\dimexpr(10\lineheight+3pt) \getnoflines\scratchdimen 11=\the\noflines \endgraf
+%D \scratchdimen\dimexpr(10\lineheight+3sp) \getnoflines\scratchdimen 10=\the\noflines \endgraf
+%D \scratchdimen\dimexpr(10\lineheight-3sp) \getnoflines\scratchdimen 10=\the\noflines \endgraf
+%D
+%D \scratchdimen\dimexpr(3pt) \getrawnoflines\scratchdimen 0=\the\noflines \endgraf
+%D \scratchdimen\dimexpr(10\lineheight) \getrawnoflines\scratchdimen 10=\the\noflines \endgraf
+%D \scratchdimen\dimexpr(10.1\lineheight) \getrawnoflines\scratchdimen 10=\the\noflines \endgraf
+%D \scratchdimen\dimexpr(10.5\lineheight) \getrawnoflines\scratchdimen 11=\the\noflines \endgraf
+%D \scratchdimen\dimexpr(10.9\lineheight) \getrawnoflines\scratchdimen 11=\the\noflines \endgraf
+%D \scratchdimen\dimexpr(10\lineheight+3pt) \getrawnoflines\scratchdimen 10=\the\noflines \endgraf
+%D \scratchdimen\dimexpr(10\lineheight+3sp) \getrawnoflines\scratchdimen 10=\the\noflines \endgraf
+%D \scratchdimen\dimexpr(10\lineheight-3sp) \getrawnoflines\scratchdimen 10=\the\noflines \endgraf
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+
+%D \macros
+%D {determinenoflines}
+%D
+%D The next macro determines the number of lines and
+%D returns it it \type {\noflines}. The macro works
+%D reasonable well as long as the content can be unboxed.
+%D
+%D \starttyping
+%D \determinenoflines{test\\test}
+%D \determinenoflines{\bfd test\\test}
+%D \determinenoflines{\definedfont[Sans at 40pt]test\\test}
+%D \stoptyping
+
+\def\determinenoflines % can be mkiv'd
+ {\bgroup
+ \forgetall
+ \let\crlf\endgraf
+ \let\\\endgraf
+ \dowithnextbox
+ {\beginofshapebox
+ \unvbox\nextbox
+ \endofshapebox
+ % \global\count1\zerocount
+ % \reshapebox{\global\advance\count1\plusone}%
+ % \egroup\noflines\count1 }%
+ \scratchcounter\zerocount
+ \reshapebox{\global\advance\scratchcounter\plusone}%
+ \expandafter\egroup\expandafter\noflines\the\scratchcounter\relax
+ }\vbox}
+
+%D \macros
+%D {doiftextelse, doiftext}
+%D
+%D When \type {\doifelse} cum suis hopelessly fail, for
+%D instance because we pass data, we can fall back on the next
+%D macro:
+%D
+%D \starttyping
+%D \doiftextelse {data} {then branch} {else branch}
+%D \doiftext {data} {then branch}
+%D \stoptyping
+
+\def\doiftextelse#1%
+ {\bgroup
+ \setbox\scratchbox\normalhbox
+ {\settrialtypesetting
+ \ignorespaces#1\removeunwantedspaces}%
+ \ifzeropt\wd\scratchbox
+ \egroup\@EA\secondoftwoarguments
+ \else
+ \egroup\@EA\firstoftwoarguments
+ \fi}
+
+\def\doiftext#1#2%
+ {\doiftextelse{#1}{#2}\donothing}
+
+%D \macros
+%D {dowithnextbox,nextbox}
+%D
+%D Sometimes we want a macro to grab a box and do something
+%D on the content. One could pass an argument to a box, but
+%D this can violate the specific \CATCODES\ of its content and
+%D leads to unexpected results. The next macro treats the
+%D following braced text as the content of a box and
+%D manipulates it afterwards in a predefined way.
+%D
+%D The first argument specifies what to do with the content.
+%D This content is available in \type{\nextbox}. The second
+%D argument is one of \type{\hbox}, \type{\vbox} or
+%D \type{\vtop}. The third argument must be grouped with
+%D \type{\bgroup} and \type{\egroup}, \type{{...}} or can be
+%D a \type{\box} specification.
+%D
+%D In \CONTEXT\ this macro is used for picking up a box and
+%D treating it according to earlier specifications. We use for
+%D instance something like:
+%D
+%D \starttyping
+%D \def\getfloat%
+%D {\def\handlefloat{...\flushnextbox...}
+%D \dowithnextbox\handlefloat\normalvbox}
+%D \stoptyping
+%D
+%D instead of:
+%D
+%D \starttyping
+%D \def\getfloat#1%
+%D {...#1...}
+%D \stoptyping
+%D
+%D In this implementation the \type{\aftergroup} construction
+%D is needed because \type{\afterassignment} is executed inside
+%D the box.
+
+\ifx\nextbox\undefined \newbox\nextbox \fi
+
+\long\def\dowithnextbox#1%
+ {\long\def\dodowithnextbox{#1}%
+ \afterassignment\dododowithnextbox
+ \setbox\nextbox}
+
+\def\dododowithnextbox
+ {\aftergroup\dodowithnextbox}
+
+\long\def\dowithnextboxcs#1%
+ {\let\dodowithnextbox#1%
+ \afterassignment\dododowithnextbox
+ \setbox\nextbox}
+
+\def\dododowithnextbox
+ {\aftergroup\dodowithnextbox}
+
+%D So in fact we get:
+%D
+%D \starttyping
+%D \setbox\nextbox { \aftergroup\dodowithnextbox ... }
+%D \stoptyping
+%D
+%D or
+%D
+%D \starttyping
+%D \setbox\nextbox { ... } \dodowithnextbox
+%D \stoptyping
+%D
+%D A slower but more versatile implementation is:
+%D
+%D \starttyping
+%D \long\def\dowithnextbox#1#2%
+%D {\long\def\dodowithnextbox{#1}%
+%D \ifx#2\normalhbox
+%D \afterassignment\dododowithnextbox
+%D \else\ifx#2\normalvbox
+%D \afterassignment\dododowithnextbox
+%D \else\ifx#2\normalvtop
+%D \afterassignment\dododowithnextbox
+%D \else\ifx#2\normalvcenter
+%D \afterassignment\dododowithnextbox
+%D \else
+%D \afterassignment\dodowithnextbox
+%D \fi\fi\fi\fi
+%D \setbox\nextbox#2}
+%D \stoptyping
+%D
+%D This alternative also accepts \type{\box0} and alike, but
+%D we don't really need this functionality now.
+
+%D \macros
+%D {nextboxht,nextboxwd,nextboxdp,flushnextbox}
+%D
+%D The next couple of shortcuts saves us memory as well as
+%D \type {{}}'s in passing parameters.
+
+\def\nextboxht{\ht\nextbox}
+\def\nextboxwd{\wd\nextbox}
+\def\nextboxdp{\dp\nextbox}
+
+\def\flushnextbox{\box\nextbox}
+
+%D \macros
+%D {dowithnextboxcontent}
+%D
+%D But, occasionally we do need to pass some local settings
+%D without wanting to use additional grouping. Therefore we
+%D provide:
+%D
+%D \starttyping
+%D \dowithnextboxcontent{inside}{after}{box content}
+%D \stoptyping
+%D
+%D {\em todo: Search source for potential usage!}
+
+\long\def\dowithnextboxcontent#1#2% inside, after
+ {\long\def\dodowithnextbox{#2}%
+ \def\dododowithnextbox{#1\aftergroup\dodowithnextbox}%
+ \afterassignment\dododowithnextbox
+ \setbox\nextbox}
+
+%D Now we can redefine \type {\dowithnextbox} as follows:
+%D
+%D \starttyping
+%D \def\dowithnextbox{\dowithnextboxcontent\empty}
+%D \stoptyping
+%D
+%D But since this macro is used often and since this implementation
+%D is slower, we will not use that definition.
+
+% maybe:
+%
+% depending on the size of the action, about 10% faster
+%
+% \newtoks\nextboxtoks
+%
+% \def\dowithnextbox {\afterassignment\redowithnextbox\nextboxtoks}
+% \def\redowithnextbox {\afterassignment\dododowithnextbox\setbox\nextbox}
+% \def\dododowithnextbox{\aftergroup\dodowithnextbox}
+% \def\dodowithnextbox {\the\nextboxtoks}
+%
+% \long\def\dowithnextboxcontent#1% #2% inside, after
+% {\def\dododowithnextbox{#1\aftergroup\dodowithnextbox}%
+% \afterassignment\redowithnextboxcontent\nextboxtoks}
+%
+% \def\redowithnextboxcontent
+% {\afterassignment\dododowithnextbox\setbox\nextbox}
+
+%D \macros
+%D {llap, rlap, tlap, blap, clap}
+%D
+%D Some well known friends, but we implement them our own
+%D way. We want the macros to work in both math and text mode.
+
+\def\dodorlap{\normalhbox to \zeropoint{\flushnextbox\normalhss}\endgroup}
+\def\dodollap{\normalhbox to \zeropoint{\normalhss\flushnextbox}\endgroup}
+\def\dodoclap{\normalhbox to \zeropoint{\normalhss\flushnextbox\normalhss}\endgroup}
+
+\def\dorlap{\begingroup\dowithnextboxcs\dodorlap\normalhbox}
+\def\dollap{\begingroup\dowithnextboxcs\dodollap\normalhbox}
+\def\doclap{\begingroup\dowithnextboxcs\dodoclap\normalhbox}
+
+\def\domathclap{\mathpalette\dodomathclap} \def\dodomathclap#1#2{\doclap{$\mathsurround\zeropoint#1#2$}}
+\def\domathllap{\mathpalette\dodomathllap} \def\dodomathllap#1#2{\dollap{$\mathsurround\zeropoint#1#2$}}
+\def\domathrlap{\mathpalette\dodomathrlap} \def\dodomathrlap#1#2{\dorlap{$\mathsurround\zeropoint#1#2$}}
+
+\unexpanded\def\rlap{\mathortext\domathrlap\dorlap}
+\unexpanded\def\llap{\mathortext\domathllap\dollap}
+\unexpanded\def\clap{\mathortext\domathclap\doclap}
+
+\def\dodotlap{\normalvbox to \zeropoint{\normalvss\flushnextbox}\endgroup}
+\def\dodoblap{\normalvbox to \zeropoint{\flushnextbox\normalvss}\endgroup}
+
+\def\tlap{\begingroup\dowithnextboxcs\dodotlap\normalvbox}
+\def\blap{\begingroup\dowithnextboxcs\dodoblap\normalvbox}
+
+%D \macros
+%D {beginofshapebox,
+%D reshapebox, doreshapebox,
+%D flushshapebox,
+%D innerflushshapebox,
+%D shapebox,
+%D ifreshapingbox}
+%D
+%D The next utility macro originates from some linenumbering
+%D mechanism. Due to \TEX's advanced way of typesetting
+%D paragraphs, it's not easy to do things on a line||by||line
+%D basis. This macro is able to reprocess a given box and can
+%D act upon its vertical boxed components, such as lines. The
+%D unwinding sequence in this macro is inspired by a \NTG\
+%D workshop of David Salomon in June 1992.
+%D
+%D First we have to grab the piece of text we want to act
+%D upon. This is done by means of the duo macros:
+%D
+%D \starttyping
+%D \beginofshapebox
+%D a piece of text
+%D \endofshapebox
+%D \stoptyping
+%D
+%D When all texts is collected, we can call \type{\reshapebox}
+%D and do something with it's vertical components. We can make
+%D as much passes as needed. When we're done, the box can be
+%D unloaded with \type{\flushshapebox}. The only condition in
+%D this scheme is that \type{\reshapebox} must somehow unload
+%D the \BOX\ \type{\shapebox}.
+%D
+%D An important aspect is that the content is unrolled
+%D bottom||up. The next example illustrates this maybe
+%D unexpected characteristic.
+%D
+%D \startbuffer
+%D \beginofshapebox
+%D \em \input tufte
+%D \endofshapebox
+%D
+%D \newcounter\LineNumber
+%D
+%D \reshapebox
+%D {\doglobal\increment\LineNumber
+%D \normalhbox{\llap{\LineNumber\hskip2em}\box\shapebox}}
+%D
+%D \flushshapebox
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D \getbuffer
+%D
+%D As we can see, when some kind of numbering is done, we have
+%D to add a second pass.
+%D
+%D \startbuffer
+%D \newcounter\LineNumber
+%D \newcounter\NumberOfLines
+%D
+%D \reshapebox
+%D {\doglobal\increment\NumberOfLines
+%D \box\shapebox}
+%D
+%D \reshapebox
+%D {\doglobal\increment\LineNumber
+%D \normalhbox
+%D {\llap{\LineNumber\ (\NumberOfLines)\hskip2em}%
+%D \box\shapebox}%
+%D \doglobal\decrement\NumberOfLines}
+%D
+%D \flushshapebox
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D \getbuffer
+%D
+%D This example shows that the content of the box is still
+%D available after flushing. Another feature is that only the
+%D last reshaping counts. Multiple reshaping can be done by:
+%D
+%D \startbuffer
+%D \beginofshapebox
+%D \flushshapebox
+%D \endofshapebox
+%D
+%D \reshapebox
+%D {\doglobal\increment\LineNumber
+%D \normalhbox{\llap{$\star$\hskip1em}\box\shapebox}%
+%D \doglobal\decrement\NumberOfLines}
+%D
+%D \flushshapebox
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D \getbuffer
+%D
+%D The macros are surprisingly easy to follow and in fact
+%D introduce no new concepts. Nearly all books on \TEX\ show
+%D similar solutions for unwinding \BOXES.
+%D
+%D Some macros, like footnote ones, can be sensitive for
+%D reshaping, which can result in an endless loop. We
+%D therefore offer:
+%D
+%D \starttyping
+%D \ifreshapingbox
+%D \stoptyping
+%D
+%D Some \CONTEXT\ commands are protected this way. Anyhow,
+%D reshaping is aborted after 100 dead cycles.
+%D
+%D By the way, changing the height and depth of \BOX\
+%D \type{\shapebox} results in bad spacing. This means that
+%D for instance linenumbers etc. should be given zero height
+%D and depth before being lapped into the margin. The
+%D previous examples ignore this side effect, but beware!
+
+\newif \ifsomeshapeleft
+\newif \ifreshapingbox
+
+\newbox \shapebox
+\newcount \shapepenalty
+\newdimen \shapekern
+\newskip \shapeskip
+
+\newbox \newshapebox
+\newbox \oldshapebox
+
+\newcount \shapecounter
+
+\newevery \everyshapebox \relax
+
+\def\shapesignal{.12345678pt} % or 12345sp
+
+% todo: in etex lastnode
+
+\def\reshapebox#1%
+ {\doreshapebox
+ {#1}%
+ {\penalty\shapepenalty}%
+ {\kern \shapekern }%
+ {\vskip \shapeskip }}
+
+\newbox\tmpshapebox
+
+\newif\ifreshapingfailed % may save redundant runs
+
+\def\doreshapebox#1#2#3#4% \shapebox, \shapepenalty, \shapekern, \shapeskip
+ {\global\reshapingfailedfalse
+ \ifzeropt\ht\oldshapebox % \ifdim\ht\oldshapebox=\zeropoint
+ \setbox\newshapebox\normalvbox{}%
+ \else
+ \setbox\newshapebox\normalvbox
+ {\unvcopy\oldshapebox
+ \resetbox\newshapebox
+ \shapecounter\zerocount
+ \doloop{\dodoreshapebox{#1}{#2}{#3}{#4}}}%
+ \setbox\newshapebox\box\tmpshapebox
+ \fi}
+
+\ifx\originalshapebox\undefined \let\originalshapebox\oldshapebox \fi
+
+% %D The old traditional tex variant:
+%
+% \def\insertshapesignal
+% {\normalhbox to \shapesignal{\strut\hss}% plus \strut
+% \prevdepth\strutdp} % never \nointerlineskip
+%
+% \def\restoreshapebox % compensates for the signal
+% {\global\setbox\tmpshapebox\vbox{\vskip-\lineheight\unvcopy\oldshapebox}}
+%
+% \def\shapeboxstrut % put this in front if needed !
+% {\vrule\!!width\zeropoint\!!height\ht\shapebox\!!depth\dp\shapebox}
+%
+% \def\dodoreshapebox#1#2#3#4% \shapebox, \shapepenalty, \shapekern, \shapeskip
+% {\ifzeropt\lastskip % \ifdim\lastskip=\zeropoint\relax
+% \ifzeropt\lastkern % \ifdim\lastkern=\zeropoint\relax
+% \ifcase\lastpenalty % \ifnum\lastpenalty=\zerocount
+% \setbox\shapebox\lastbox
+% \ifvoid\shapebox
+% \unskip\unpenalty\unkern
+% \else
+% \ifdim\wd\shapebox=\shapesignal\relax
+% \exitloop
+% \else
+% \shapecounter\zerocount
+% \global\setbox\tmpshapebox\normalvbox{#1\unvbox\tmpshapebox}%
+% \fi
+% \fi
+% \else
+% \shapepenalty\lastpenalty
+% \global\setbox\tmpshapebox\normalvbox{#2\unvbox\tmpshapebox}%
+% \unpenalty
+% \fi
+% \else
+% \shapekern\lastkern
+% \global\setbox\tmpshapebox\normalvbox{#3\unvbox\tmpshapebox}%
+% \unkern
+% \fi
+% \else
+% \shapeskip\lastskip
+% \global\setbox\tmpshapebox\normalvbox{#4\unvbox\tmpshapebox}%
+% \unskip
+% \fi
+% \ifnum\shapecounter>100 % can be less
+% \global\reshapingfailedtrue
+% \message{!!forced exit from shapebox!!}%
+% \restoreshapebox
+% \exitloop
+% \else
+% \advance\shapecounter \plusone
+% \fi}
+%
+% But now that the lastnode bugfixes are wide spread we can use:
+%
+% We will turn this into a \MKIV\ variant.
+
+\def\insertshapesignal
+ {\normalhbox to \shapesignal{\strut\hss}% plus \strut
+ \prevdepth\strutdp} % never \nointerlineskip
+
+\def\restoreshapebox % compensates for the signal
+ {\global\setbox\tmpshapebox\vbox{\vskip-\lineheight\unvcopy\oldshapebox}}
+
+\def\dodoreshapebox#1#2#3#4% \shapebox, \shapepenalty, \shapekern, \shapeskip
+ {\ifnum\lastnodetype=\@@gluenode
+ \shapeskip\lastskip
+ \global\setbox\tmpshapebox\normalvbox{#4\unvbox\tmpshapebox}%
+ \unskip
+ \else\ifnum\lastnodetype=\@@kernnode
+ \shapekern\lastkern
+ \global\setbox\tmpshapebox\normalvbox{#3\unvbox\tmpshapebox}%
+ \unkern
+ \else\ifnum\lastnodetype=\@@penaltynode
+ \shapepenalty\lastpenalty
+ \global\setbox\tmpshapebox\normalvbox{#2\unvbox\tmpshapebox}%
+ \unpenalty
+ \else\ifnum\lastnodetype<\zeropoint
+ \exitloop
+ \else
+ \setbox\shapebox\lastbox
+ \ifvoid\shapebox
+ \else\ifdim\wd\shapebox=\shapesignal\relax
+ \exitloop
+ \else
+ \shapecounter\zerocount
+ \global\setbox\tmpshapebox\normalvbox{#1\unvbox\tmpshapebox}%
+ \fi\fi
+ \fi\fi\fi\fi
+ \ifnum\shapecounter>100 % can be less
+ \global\reshapingfailedtrue
+ \message{!!forced exit from shapebox \the\lastnodetype !!}%
+ \restoreshapebox
+ \exitloop
+ \else
+ \advance\shapecounter \plusone
+ \fi}
+
+\def\beginofshapebox
+ {\setbox\oldshapebox\normalvbox
+ \bgroup
+ \reshapingboxtrue
+ \the\everyshapebox
+ \insertshapesignal}
+
+\def\endofshapebox
+ {\endgraf
+ \egroup}
+
+\let\beginshapebox\beginofshapebox
+\let\endshapebox \endofshapebox
+
+\def\flushshapebox
+ {\bgroup
+ \ifzeropt\ht\newshapebox % \ifdim\ht\newshapebox=\zeropoint
+ \else
+ % make \prevdepth legal
+ % \par before the next \vskip gives far worse results
+ \ifdim\parskip>\zeropoint\vskip\parskip\else\par\fi
+ % and take a look
+ \ifdim\prevdepth=-\thousandpoint
+ \prevdepth\zeropoint
+ \fi
+ \ifdim\prevdepth<\zeropoint\relax
+ % something like a line or a signal or ...
+ \donetrue
+ \else\ifinner
+ % not watertight and not ok
+ \donefalse
+ \else\ifdim\pagegoal=\maxdimen
+ \donetrue
+ \else
+ % give the previous line a normal depth
+ \donetrue
+ {\forgeteverypar\verticalstrut}\nobreak
+ \kern-\struttotal % geen \vskip
+ \kern-\parskip
+ % \vskip-\strutdp
+ \fi\fi\fi
+ \scratchdimen\dp\newshapebox
+ \unvbox\newshapebox
+ % \prevdepth=0pt and \dp\newshapebox depend on last line
+ \kern-\scratchdimen % ??
+ % now \prevdepth=0pt
+ \ifdone
+ \kern\strutdp
+ \prevdepth\strutdp
+ \fi
+ \fi
+ \egroup}
+
+%D In real inner situations we can use:
+%D
+%D \starttyping
+%D \flushinnershapebox
+%D \stoptyping
+%D
+%D This one is used in \type{\framed}.
+
+% The kern fails on for instance:
+%
+% \omlijnd[offset=0pt,hoogte=8mm,uitlijnen={rechts,laho}]{\bfa test}
+
+\def\innerflushshapebox
+ {\ifzeropt\ht\newshapebox \else
+ \unvcopy\newshapebox\relax % unvcopy ! else spacing problem
+ % \kern-\dp\newshapebox\relax
+ \fi}
+
+%D For absolute control, one can use \type{\doreshapebox}
+%D directly. This macro takes four arguments, that take care
+%D of:
+%D
+%D \startitemize[n,packed]
+%D \item \type{\shapebox}
+%D \item \type{\shapepenalty}
+%D \item \type{\shapekern}
+%D \item \type{\shapeskip}
+%D \stopitemize
+
+%D \macros
+%D {shapedhbox}
+%D
+%D When constructing a new box, using the content of \type
+%D {\shapebox}, one can best use \type {\shapedhbox} instead
+%D of \type {\normalhbox}, since it manages the height and depth of
+%D the line.
+
+% \def\shapedhbox
+% {\dowithnextbox
+% {\nextboxht\zeropoint
+% \nextboxdp\zeropoint
+% \flushnextbox}
+% \normalhbox}
+
+\def\shapedhbox % lines with non strutted dimensions have
+ {\expanded{\dowithnextbox % interlineskip so if we want the original
+ {\nextboxht\the\ht\shapebox % spacing, we need to preserve the original
+ \nextboxdp\the\dp\shapebox % height and depth which is definitely
+ \noexpand\flushnextbox}} % needed if we apply struts to the 'new'
+ \normalhbox} % box or do something that changed ist size
+
+%D \macros
+%D {hyphenatedword,
+%D hyphenatedpar,
+%D hyphenatedfile,
+%D dohyphenateword}
+%D
+%D The next one is a tricky one. \PLAIN\ \TEX\ provides
+%D \type{\showhyphens} for showing macros on the terminal. When
+%D preparing a long list of words we decided to show the
+%D hyphens, but had to find out that the \PLAIN\ alternative
+%D can hardly be used and|/|or adapted to typesetting. The next
+%D two macros do the job and a little more. First we define the
+%D (slightly adapted) plain variant:
+
+\def\showhyphens#1%
+ {\begingroup
+ \setbox\scratchbox\vbox
+ {\parfillskip\zerocount
+ \hsize\maxdimen
+ %\tenrm
+ \pretolerance\minusone
+ \tolerance\minusone
+ \hbadness\zerocount
+ \showboxdepth\zerocount
+ \ #1}%
+ \endgroup}
+
+%D The simple command \type{\hyphenatedword} accepts one
+%D argument and gives the hyphenated word. This macro calls for
+%D
+%D \starttyping
+%D \dohyphenateword {n} {pre} {word}
+%D \stoptyping
+%D
+%D The next examples tell more than lots of words:
+%D
+%D \startbuffer
+%D \dohyphenateword{0} {} {dohyphenatedword}
+%D \dohyphenateword{1} {...} {dohyphenatedword}
+%D \dohyphenateword{2} {...} {dohyphenatedword}
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D Here, \type{\hyphenatedword{dohyphenatedword}} is the
+%D shorter alternative for the first line.
+%D
+%D \startvoorbeeld
+%D \getbuffer
+%D \stopvoorbeeld
+%D
+%D These macros are slow but effective and not that hard to
+%D program at all.
+
+\ifx\scantokens\undefined \let\scantokens\firstofoneargument \fi
+
+\def\dohyphenateword#1#2#3%
+ {\bgroup
+ \setbox\scratchbox\normalhbox
+ {\dontcomplain
+ \nopenalties % \widowpenalty \clubpenalty \brokenpenalty \doublehyphendemerits \finalhyphendemerits \adjdemerits
+ \hyphenpenalty \zerocount
+ \exhyphenpenalty\zerocount
+ \setbox0\normalvbox
+ {\hsize\zeropoint
+ \hskip\zeropoint\relax % really needed
+ \ifnum#1<\zeropoint
+ \obeyspaces
+ \obeylines
+ \def\obeyedspace{\hskip\zeropoint\hbox to \onepoint{}\hskip\zeropoint}%
+ \let\obeyedline \obeyedspace
+ \ifcase-#1\or
+ \def\next{#3\relax}\scantokens\expandafter{\next}% relax catches lookahead problem
+ % also ok: \scantokens{#3}% % as in \hyphenatedword{spanish|?|}
+ \or
+ \readfile{#3}\donothing\donothing
+ \else
+ #3%
+ \fi
+ \else
+ #3%
+ \fi}%
+ \ifnum#1>\zerocount
+ \dorecurse{#1}
+ {\setbox2\normalhbox
+ {\splittopskip\openstrutheight
+ \vsplit0 to \baselineskip}}%
+ #2%
+ \fi
+ \doloop
+ {\setbox2\normalhbox
+ {\splittopskip\openstrutheight
+ \vsplit0 to \baselineskip}%
+ \setbox2\normalhbox
+ {\unhbox2
+ \setbox2\lastbox
+ \normalvbox
+ {\unvbox2
+ \setbox2\lastbox
+ \normalhbox{\unhbox2}}}%
+ \ifnum#1<\zeropoint\ifdim\wd2=\onepoint\space\else\box2\allowbreak\fi\else\box2\fi
+ \ifzeropt\ht0 \exitloop\fi}% % \ifdim\ht0=\zeropoint\exitloop\fi}%
+ \removeunwantedspaces}%
+ \ifnum#1>\zerocount
+ \ht\scratchbox\strutht
+ \dp\scratchbox\strutdp
+ \box\scratchbox
+ \else
+ \unhbox\scratchbox
+ \fi
+ \egroup}
+
+\def\hyphenatedword{\dohyphenateword\zerocount\empty}
+\def\hyphenatedpar {\dohyphenateword\minusone \empty}
+\def\hyphenatedfile{\dohyphenateword{-2}\empty}
+
+%D You may want to give the following call a try:
+%D
+%D \starttyping
+%D \hyphenatedpar{\readfile{zapf}{}{}}\endgraf
+%D \stoptyping
+
+%D \macros
+%D {processtokens}
+%D
+%D We fully agree with (most) typographers that inter||letter
+%D spacing is only permitted in fancy titles, we provide a
+%D macro that can be used to do so. Because this is
+%D (definitely and fortunately) no feature of \TEX, we have to
+%D step through the token list ourselves.
+%D
+%D \starttyping
+%D \processtokens {before} {between} {after} {space} {tokens}
+%D \stoptyping
+%D
+%D An example of a call is:
+%D
+%D \startbuffer
+%D \processtokens {[} {+} {]} {\space} {hello world}
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D This results in:
+%D
+%D \getbuffer
+%D
+%D The list of tokens may contain spaces, while \type{\\},
+%D \type{{}} and \type{\ } are handled as space too.
+
+\def\dodoprocesstokens
+ {\ifx\nextprocessedtoken\lastcharacter
+ \after
+ \let\nextprocessedtoken\relax
+ \else\ifx\nextprocessedtoken\bgroup
+ \def\nextprocessedtoken
+ {\dowithnextbox
+ {\before{\copy\nextbox}% \before can use nextbox several times
+ \let\before\between
+ \doprocesstokens}
+ \hbox\bgroup}%
+ \else
+ \expandafter\if\space\nextprocessedtoken
+ \after\white
+ \let\before\savedbefore
+ \else
+ \before\nextprocessedtoken
+ \let\before\between
+ \fi
+ \let\nextprocessedtoken\doprocesstokens
+ \fi\fi
+ \nextprocessedtoken}
+
+\def\doprocesstokens% the space after = is essential
+ {\afterassignment\dodoprocesstokens\let\nextprocessedtoken= }
+
+\def\processtokens#1#2#3#4#5%
+ {\begingroup
+ \def\lastcharacter{\lastcharacter}%
+ \def\space{ }%
+ \let\\=\space
+ \def\before {#1}%
+ \def\between{#2}%
+ \def\after {#3}%
+ \def\white {#4}%
+ \let\savedbefore\before
+ \doprocesstokens#5\lastcharacter
+ \endgroup}
+
+%D \macros
+%D {doboundtext}
+%D
+%D Sometimes there is not enough room to show the complete
+%D (line of) text. In such a situation we can strip of some
+%D characters by using \type{\doboundtext}. When the text is
+%D wider than the given width, it's split and the third
+%D argument is appended. When the text to be checked is packed
+%D in a command, we'll have to use \type{\expandafter}.
+%D
+%D \starttyping
+%D \doboundtext{a very, probably to long, text}{3cm}{...}
+%D \stoptyping
+%D
+%D When calculating the room needed, we take the width of the
+%D third argument into account, which leads to a bit more
+%D complex macro than needed at first sight.
+
+% \def\dodoboundtext#1%
+% {\setbox0=\normalhbox{\unhcopy0 #1}%
+% \ifdim\wd0>\dimen0
+% \let\dodoboundtext=\gobbleoneargument
+% \else
+% #1\relax
+% \fi}
+%
+% \def\doboundtext#1#2#3%
+% {\normalhbox
+% {\setbox0=\normalhbox{#1}%
+% \dimen0=#2\relax
+% \ifdim\wd0>\dimen0
+% \setbox2=\normalhbox{#3}%
+% \advance\dimen0 by -\wd2
+% \setbox0=\normalhbox{}%
+% \processtokens
+% {\dodoboundtext}
+% {\dodoboundtext}
+% {}
+% {\space}
+% {#1}%
+% \box2
+% \else
+% \box0
+% \fi}}
+
+\def\dodoboundtext#1%
+ {\setbox0\normalhbox{#1}%
+ \advance\scratchdimen -\wd0
+ \ifdim\scratchdimen>\zeropoint\relax#1\fi}%
+
+\def\doboundtext#1#2#3%
+ {\normalhbox
+ {\setbox\scratchbox\normalhbox{#1}%
+ \scratchdimen#2\relax
+ \ifdim\wd\scratchbox>\scratchdimen
+ \setbox\scratchbox\normalhbox{#3}%
+ \advance\scratchdimen -\wd\scratchbox
+ \handletokens#1\with\dodoboundtext
+ \fi
+ \box\scratchbox}}
+
+%D \macros
+%D {limitatetext}
+%D
+%D A bit more beautiful alternative for the previous command is
+%D the next one. This command is more robust because we let
+%D \TEX\ do most of the job. The previous command works better
+%D on text that cannot be hyphenated.
+%D
+%D \starttyping
+%D \limitatetext {text} {width} {sentinel}
+%D \limitatetext {text} {-width} {prelude}
+%D \stoptyping
+%D
+%D When no width is given, the whole text comes available. The
+%D sentinel is optional. This is about the third version.
+
+\ifx\fakecompoundhyphen\undefined \let\fakecompoundhyphen\relax \fi
+\ifx\veryraggedright \undefined \def\veryraggedright{\raggedright} \fi
+
+%D The simple alternative is as follows:
+%D
+%D \starttyping
+%D \unexpanded\def\limitatetext%
+%D {\bgroup % evt \setstrut
+%D \forgetall
+%D \fakecompoundhyphen % dangerous ! ! ! ! ! ! ! ! !
+%D \dowithnextbox\dolimitatetext\normalhbox}
+%D
+%D \def\dolimitatetext#1#2%
+%D {\doifelsenothing{#1}
+%D {\unhbox\nextbox}
+%D {\widowpenalty=0
+%D \clubpenalty=0
+%D \scratchdimen=#1\relax
+%D \ifdim\nextboxwd>\scratchdimen
+%D \setbox\scratchbox=\normalhbox{ #2}%
+%D \advance\scratchdimen by -\wd\scratchbox
+%D \setbox\nextbox=\normalvbox
+%D {\hsize=\scratchdimen
+%D \hfuzz\maxdimen
+%D \veryraggedright
+%D \strut\unhcopy\nextbox}%
+%D \ifdim\nextboxht>\strutht \else
+%D \setbox\scratchbox\null % overfull and not split
+%D \fi
+%D \setbox\nextbox=\normalvbox % if omitted: missing brace reported
+%D {\splittopskip=\openstrutheight
+%D \setbox\nextbox=\vsplit\nextbox to \strutht
+%D \unvbox\nextbox
+%D \setbox\nextbox=\lastbox
+%D \global\setbox1=\normalhbox
+%D {\unhbox\nextbox\unskip\kern\zeropoint\box\scratchbox\unskip}}%
+%D \unhbox1
+%D \else
+%D \unhbox\nextbox
+%D \fi}%
+%D \egroup}
+%D \stoptyping
+%D
+%D The next alternative accepts a negative width. A negative
+%D value crops the beginning. The macro thereby becomes less
+%D readable, which is why we kept the original here too.
+
+\unexpanded\def\limitatetext
+ {\bgroup % evt \setstrut
+ \forgetall % otherwise indentation and so
+ %\def\limitatetext##1##2##3{##1}% \def !
+ \let\limitatetext\firstofthreearguments
+ \fakecompoundhyphen % dangerous ! ! ! ! ! ! ! ! !
+ \dowithnextboxcs\dolimitatetext\normalhbox}
+
+\def\dolimitatetext#1#2%
+ {\doifelsenothing{#1}
+ {\unhbox\nextbox}
+ {\nopenalties
+ \scratchdimen#1\relax
+ \ifdim\scratchdimen<\zeropoint\relax % we'll take the last line
+ \donefalse
+ \scratchdimen-\scratchdimen
+ \else
+ \donetrue
+ \fi
+ \ifdim\nextboxwd>\scratchdimen
+ \setbox\scratchbox\normalhbox{\ifdone\space#2\else#2\space\fi}%
+ \advance\scratchdimen -\wd\scratchbox
+ \setbox0\flushnextbox
+ \setbox\nextbox\normalvbox
+ {\hsize\scratchdimen
+ \hfuzz\maxdimen
+ \veryraggedright
+ \strut
+ \ifdone \else
+ \parfillskip\zeropoint
+ \rightskip\zeropoint
+ \hskip\zeropoint \!!plus 1\!!fill % \hsize
+ \fi
+ \unhcopy0}%
+ \ifdim\nextboxht>\strutht
+ \setbox\nextbox\normalvbox % if omitted: missing brace reported
+ {\splittopskip\openstrutheight
+ \ifdone
+ \setbox\nextbox\vsplit\nextbox to \strutht
+ \else
+ \doloop
+ {\setbox0\vsplit\nextbox to \strutht
+ \ifdim\nextboxht>\strutht \else \exitloop \fi}%
+ \fi
+ \unvbox\nextbox
+ \setbox\nextbox\lastbox
+ \global\setbox1\normalhbox
+ {\ifdone
+ \unhbox\nextbox\unskip\kern\zeropoint\box\scratchbox
+ \else
+ \box\scratchbox\unhbox\nextbox
+ \fi
+ \unskip}}%
+ \unhbox1
+ \else
+ \unhbox0
+ \fi
+ \else
+ \unhbox\nextbox
+ \fi}%
+ \egroup}
+
+%D We can also limit a text with more control:
+%D
+%D \startbuffer
+%D \limitatetext {\input tufte } {2cm,5mm} {\unknown}
+%D \limitatetext {ton en hans} {2cm,5mm} {\unknown}
+%D \limitatetext {ton en hans zijn eikels} {2cm,5mm} {\unknown}
+%D \limitatetext {ton} {2cm,5mm} {\unknown}
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+%D
+%D We build this feature on top of the previous macro.
+
+\let\normallimitatetext\limitatetext
+
+\def\speciallimitatetext#1#2#3#4% text left right placeholder
+ {%\dontleavehmode
+ \bgroup
+ %\def\speciallimitatetext##1##2##3##4{##1}% \def !
+ \let\speciallimitatetext\firstoffourarguments
+ \setbox0\normalhbox
+ {\nohyphens
+ \normallimitatetext{#1}{+#2}{}#4%
+ \normallimitatetext{#1}{-#3}{}}%
+ \setbox2\normalhbox
+ {#1}%
+ \ifdim\wd2<\wd0 #1\else\unhbox0\fi
+ \egroup}
+
+\def\limitatetext#1#2#3% \expanded added 2003/01/16
+ {\expanded{\beforesplitstring#2}\at,\to\leftlimit
+ \expanded{\aftersplitstring #2}\at,\to\rightlimit
+ \ifx\rightlimit\empty
+ \normallimitatetext {#1}\leftlimit {#3}%
+ \else
+ \speciallimitatetext{#1}\leftlimit\rightlimit{#3}%
+ \fi}
+
+%D Undocumented bonus (see wiki):
+%D
+%D \starttyping
+%D \limitatefirstline{\input tufte\relax}{10cm}{\unknown}
+%D \stoptyping
+
+\def\limitatefirstline#1#2#3%
+ {\hbox\bgroup\strut
+ \setbox\scratchbox\hbox{\begstrut#1\endstrut}%
+ \ifdim\wd\scratchbox>#2\relax
+ \setbox\scratchbox\hbox{#3}%
+ \hsize#2\relax
+ \advance\hsize-\wd\scratchbox
+ \setbox\scratchbox\vbox{\forgetall\veryraggedright#1}%
+ \setbox\scratchbox\vsplit\scratchbox to \lineheight
+ \vbox
+ {\unvbox\scratchbox
+ \global\setbox\plusone\lastbox
+ \global\setbox\plusone\hbox{\strut\unhbox\plusone}%
+ \hbox % to #2
+ {\ifx\clip\undefined
+ \box\plusone
+ \else\ifdim\wd\plusone>\hsize
+ \lower\strutdepth\hbox{\clip[\c!width=\hsize,\c!height=\lineheight]{\hbox{\raise\strutdepth\box\plusone}}}%
+ \else
+ \box\plusone
+ \fi\fi
+ \removeunwantedspaces#3}}% \removeunwantedspaces\hss#3}}%
+ \else
+ #1%
+ \fi
+ \egroup}
+
+%D \macros
+%D {processisolatedwords,
+%D betweenisolatedwords,nothingbetweenisolatedwords}
+%D
+%D References are often made up of one word or a combination
+%D of tightly connected words. The typeset text {\bf
+%D chapter~5} is for instance the results of the character
+%D sequence:
+%D
+%D \starttyping
+%D The typeset text \in{chapter}[texniques] is for instance
+%D \stoptyping
+%D
+%D When such words are made active in interactive texts, the
+%D combination cannot longer be hyphenated. Normally this is no
+%D problem, because \TEX\ tries to prevent hyphenation as best
+%D as can.
+%D
+%D Sometimes however we need a few more words to make things
+%D clear, like when we want to refer to {\bf \TEX\ by Topic}.
+%D The macros that are responsible for typesetting hyperlinks,
+%D take care of such sub||sentences by breaking them up in
+%D words. Long ago we processed words using the space as a
+%D separator, but the more advanced our interactive text became,
+%D the more we needed a robust solution. Well, here it is and
+%D it called as:
+%D
+%D \starttyping
+%D \processisolatedwords{some words}\someaction
+%D \stoptyping
+%D
+%D The second argument \type{someactions} handles the
+%D individual words, like in:
+%D
+%D \startbuffer
+%D \processisolatedwords{some more words} \ruledhbox \par
+%D \processisolatedwords{and some $x + y = z$ math} \ruledhbox \par
+%D \processisolatedwords{and a \normalhbox{$x + y = z$}} \ruledhbox \par
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D which let the words turn up as:
+%D
+%D \startvoorbeeld
+%D \getbuffer
+%D \stopvoorbeeld
+%D
+%D The macro has been made a bit more clever than needed at
+%D first sight. This is due to the fact that we don't want to
+%D generate more overhead in terms of interactive commands than
+%D needed.
+%D
+%D \startbuffer
+%D \processisolatedwords{see this \ruledhskip1em} \ruledhbox
+%D \processisolatedwords{and \ruledhskip1em this one} \ruledhbox
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D becomes:
+%D
+%D \startvoorbeeld
+%D \startlines
+%D \getbuffer
+%D \stoplines
+%D \stopvoorbeeld
+%D
+%D Single word arguments are treated without further
+%D processing. This was needed because this command is used in
+%D the \type{\goto} command, to which we sometimes pass very
+%D strange and|/|or complicated arguments or simply boxes
+%D whose dimensions are to be left intact.
+%D
+%D First we build a \type{\normalhbox}. This enables us to save the
+%D last skip. Next we fill a \type{\normalvbox} without hyphenating
+%D words. After we've tested if there is more than one word, we
+%D start processing the individual lines (words). We need some
+%D splitting, packing and unpacking to get the spacing and
+%D dimensions right.
+%D
+%D Normally the isolated words are separated by space, but
+%D one can overrule this separator by changing the next macros.
+%D
+%D When needed, spacing can be suppressed by \type
+%D {\nothingbetweenisolatedwords}.
+
+\newif\ifisolatedwords % public, e.g. used in core-ref
+
+\def\betweenisolatedwords
+ {\hskip\currentspaceskip}
+
+%D In order to prevent problems with nested isolated words, we
+%D do process them, but only split at the outermost level.
+
+\newskip\isolatedlastskip
+
+\chardef\isolatedwordsmode=0 % no nesting
+
+\def\processisolatedwords#1#2% todo: vbox ipv hbox ivm afbreken!
+ {\bgroup % todo: doloop
+ \fakecompoundhyphen
+ \dontcomplain
+ \forgetall
+ \nopenalties
+ \ifcase\isolatedwordsmode
+ \def\processisolatedwords##1##2{##2{##1}}% we split only once
+ \fi
+ \global\let\localbetweenisolatedwords\betweenisolatedwords
+ \setbox0\normalhbox % we default to spaces, but from inside out
+ {\normallanguage\minusone % needed for mkiv
+ \ignorespaces#1% \localbetweenisolatedwords can be overruled
+ \global\isolatedlastskip\lastskip}%
+ \setbox2\normalvbox
+ {%\hyphenpenalty10000 % this one fails in \url breaking,
+ \lefthyphenmin\maxcard % but this trick works ok, due to them
+ \righthyphenmin\maxcard % total>63, when no hyphenation is done
+ \hsize\zeropoint
+ \unhcopy0}% == #1
+ \ifdim\ht0=\ht2
+ \isolatedwordsfalse
+ #2{\unhbox0}% == #2{#1} % was \unhcopy0
+ \else
+ \isolatedwordstrue
+ \setbox0\normalhbox
+ {\ignorespaces
+ \loop
+ \setbox4\normalhbox
+ {\splittopskip\openstrutheight
+ \vsplit2 to \baselineskip}%
+ \normalhbox
+ {\unhbox4\unskip % recently added
+ \setbox4\lastbox
+ \normalvbox % outer \normalhbox needed
+ {\unvbox4 % for nested use
+ \setbox4\lastbox
+ \normalhbox{#2{\normalhbox
+ {\unhbox4
+ \unskip\unpenalty % remove end of line stuff
+ \global\dimen1\lastkern}}}}}%
+ \ifdim\ht2>\zeropoint\relax
+ \ifdim\dimen1=\compoundbreakpoint
+ \allowbreak
+ \else
+ \localbetweenisolatedwords
+ \fi
+ \repeat
+ \unskip}%
+ \unhbox0\unskip
+ \ifzeropt\isolatedlastskip\else % added % \ifdim\isolatedlastskip=\zeropoint\else % added
+ \hskip\isolatedlastskip
+ \fi
+ \fi
+ \egroup}
+
+%D One can use the next macro to change the intersplit
+%D material. An example can be found in the \type {\url}
+%D macro. The innermost setting is used. In the url case, it
+%D means that either very small spaces are used or no spaces
+%D at all. So, the innermost settings are used, while the
+%D outermost split takes place.
+
+\def\setbetweenisolatedwords#1%
+ {\gdef\localbetweenisolatedwords{#1}}
+
+%D \macros
+%D {sbox}
+%D
+%D This is a rather strange command. It grabs some box content
+%D and and limits the size to the height and depth of a
+%D \type{\strut}. The resulting bottom||alligned box can be used
+%D aside other ones, without disturbing the normal baseline
+%D distance.
+%D
+%D \startbuffer
+%D \ruledhbox to .5\hsize{\sbox{eerste\par tweede \par derde}}
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D Shows up as:
+%D
+%D \startvoorbeeld
+%D \vskip3\baselineskip
+%D \getbuffer
+%D \stopvoorbeeld
+%D
+%D Before displaying the result we added some skip, otherwise
+%D the first two lines would have ended up in the text. This
+%D macro can be useful when building complicated menus, headers
+%D and footers and|/|or margin material.
+
+\def\sbox% in handleiding, voorbeeld \inleft{xx} \extern..
+ {\normalvbox\bgroup % new ! ! !
+ \dowithnextbox
+ {\setbox\scratchbox\normalhbox
+ {\strut
+ \nextboxdp\zeropoint
+ \lower\strutdepth\flushnextbox}%
+ \dp\scratchbox\strutdepth
+ \ht\scratchbox\strutheight
+ \box\scratchbox
+ \egroup}%
+ \normalvbox}
+
+%D \macros
+%D {struttedbox}
+%D
+%D This boxing macro limits the height and depth to those of
+%D a strut.
+
+\def\struttedbox
+ {\normalhbox\bgroup % new ! ! !
+ \dowithnextbox
+ {\nextboxdp\strutdepth
+ \nextboxht\strutheight
+ \flushnextbox
+ \egroup}%
+ \normalhbox}
+
+%D \macros
+%D {topskippedbox}
+%D
+%D This macro compensates the difference between the topskip
+%D and strutheight. Watch how we preserve the depth when it
+%D equals strutdepth.
+
+\def\topskippedbox
+ {\normalhbox\bgroup
+ \dowithnextbox
+ {\edef\next
+ {\ifdim\strutdepth=\nextboxdp\nextboxdp\the\nextboxdp\fi}%
+ \lower\topskip\normalhbox{\raise\strutheight\flushnextbox}%
+ \next
+ \egroup}%
+ \normalhbox}
+
+%D \macros
+%D {centeredbox, centerednextbox}
+%D
+%D Here is another strange one. This one offers a sort of overlay
+%D with positive or negative offsets. This command can be used
+%D in well defined areas where no offset options are available.
+%D We first used it when building a button inside the margin
+%D footer, where the button should have a horizontal offset and
+%D should be centered with respect to the surrounding box. The
+%D last of the three examples we show below says:
+%D
+%D \starttyping
+%D \vsize=3cm
+%D \hsize=3cm
+%D \ruledvbox to \vsize
+%D {\centeredbox height .5cm width -1cm
+%D {\vrule width \hsize height \vsize}}}
+%D \stoptyping
+%D
+%D Here the \type{\ruledvbox} just shows the surrounding box
+%D and \type{\vrule} is used to show the centered box.
+%D
+%D \def\AnExample#1#2%
+%D {\vsize=3cm
+%D \hsize=3cm
+%D \ruledvbox to \vsize
+%D {\centeredbox height #1 width #2
+%D {\color[green]{\vrule width \hsize height \vsize}}}}
+%D
+%D \startlinecorrection
+%D \startcombination[3*1]
+%D {\AnExample {-1cm} {.5cm}} {}
+%D {\AnExample {.5cm} {-1cm}} {}
+%D {\AnExample {-1cm} {-.5cm}} {}
+%D \stopcombination
+%D \stoplinecorrection
+%D
+%D This command takes two optional arguments: \type{width} and
+%D \type{height}. Observing readers can see that we use \TEX's
+%D own scanner for grabbing these arguments: \type{#1#} reads
+%D everyting till the next brace and passes it to both rules.
+%D The setting of the box dimensions at the end is needed for
+%D special cases. The dimensions of the surrounding box are kept
+%D intact. This commands handles positive and negative
+%D dimensions (which is why we need two boxes with rules).
+
+\def\centeredbox#1#% height +/-dimen width +/-dimen
+ {\bgroup
+ \setbox0\normalvbox to \vsize
+ \bgroup
+ \dontcomplain
+ \forgetall
+ \setbox0\normalhbox{\vrule\!!width \zeropoint#1}%
+ \setbox2\normalvbox{\hrule\!!height\zeropoint#1}%
+ \advance\vsize \ht2
+ \advance\hsize \wd0
+ \normalvbox to \vsize
+ \bgroup
+ \vskip-\ht2
+ \vss
+ \normalhbox to \hsize
+ \bgroup
+ \dowithnextbox
+ {\hskip-\wd0
+ \hss
+ \flushnextbox
+ \hss
+ \egroup
+ \vss
+ \egroup
+ \egroup
+ \wd0\hsize
+ \ht0\vsize
+ \box0
+ \egroup}
+ \normalhbox}
+
+%D For those who don't want to deal with \type {\hsize}
+%D and \type {\vsize}, we have:
+%D
+%D \starttyping
+%D \centerednextbox width 2bp height 2bp
+%D {\framed[width=100bp,height=100bp]{}}
+%D \stoptyping
+%D
+%D Do you see what we call this one \type {next}?
+
+\def\centerednextbox#1#%
+ {\bgroup
+ \dowithnextbox
+ {\hsize\nextboxwd
+ \vsize\nextboxht
+ \centeredbox#1{\flushnextbox}%
+ \egroup}
+ \normalhbox}
+
+%D \macros
+%D {centerbox}
+%D
+%D Centering on the available space is done by:
+%D
+%D \starttyping
+%D \centerbox <optional specs> {content}
+%D \stoptyping
+%D
+%D When omitted, the current \type {\hsize} and \type
+%D {\vsize} are used. Local dimensions are supported.
+
+\long\def\centerbox#1#% optional height +/-dimen width +/-dimen
+ {\bgroup
+ \dowithnextbox
+ {\setlocalhsize
+ \setbox0\normalhbox{\vrule\!!width \zeropoint#1}%
+ \setbox2\normalvbox{\hrule\!!height\zeropoint#1}%
+ \ifzeropt\wd0\else\hsize\wd0\fi % \hsize\ifdim\wd0=\zeropoint\hsize\else\wd0\fi
+ \ifzeropt\ht2\else\vsize\ht2\fi % \vsize\ifdim\ht2=\zeropoint\vsize\else\ht2\fi
+ \normalvbox to \vsize{\vss\normalhbox to \hsize{\hss\flushnextbox\hss}\vss}%
+ \egroup}%
+ \normalhbox}
+
+%D \macros
+%D {setrigidcolumnhsize,rigidcolumnbalance,rigidcolumnlines}
+%D
+%D These macros are copied from the \TEX book, page~397, and
+%D extended by a macro that sets the \type{\hsize}.
+%D
+%D \starttyping
+%D \setrigidcolumnhsize {total width} {distance} {n}
+%D \rigidcolumnbalance {box}
+%D \stoptyping
+%D
+%D Both these macros are for instance used in typesetting
+%D footnotes.
+%D
+%D Men kan het proces van breken enigzins beinvloeden met de
+%D volgende twee switches:
+
+\newif\ifalignrigidcolumns
+\newif\ifstretchrigidcolumns
+\newif\iftightrigidcolumns % if true: just a vbox, no depth/noflines/gridsnap corrrections
+
+%D De eerste switch bepaald het uitlijnen, de tweede rekt de
+%D individuele kolommen op naar \type{\vsize}.
+
+\def\setrigidcolumnhsize#1#2#3% todo: \dimexpr
+ {\xdef\savedrigidhsize{\the\hsize}%
+ \hsize#1\relax
+ \global\chardef\rigidcolumns#3\relax
+ \scratchdimen -#2\relax
+ \multiply\scratchdimen #3\relax
+ \advance\scratchdimen #2\relax
+ \advance\hsize \scratchdimen
+ \divide\hsize #3\relax}
+
+% ==
+%
+% \def\setrigidcolumnhsize#1#2#3%
+% {\xdef\savedrigidhsize{\the\hsize}%
+% \global\chardef\rigidcolumns#3\relax
+% \hsize=\dimexpr(#1-\numexpr#3-1\relax\dimexpr#2\relax)/#3\relax}
+
+\newbox\rigidcolumnbox
+
+\let\rigidcolumnlines\!!zerocount
+
+\def\rigidcolumnbalance#1%
+ {\ifnum\rigidcolumns=1 % tzt ook h/d correctie
+ \ifinner\ifhmode\box\else\unvbox\fi\else\unvbox\fi#1\relax
+ \else
+ \normalvbox
+ {\forgetall
+ \nopenalties
+ \dontcomplain
+ \setbox\rigidcolumnbox\normalvbox
+ {\line{}\goodbreak\unvbox#1\removebottomthings}%
+ \splittopskip\openstrutheight
+ \setbox\scratchbox\vsplit\rigidcolumnbox to \zeropoint
+ \ifcase\rigidcolumnlines\relax
+ % \iffalse
+ % % maybe some day an option
+ % \scratchskip\ht\rigidcolumnbox
+ % \advance\scratchskip\dp\rigidcolumnbox
+ % \getnoflines\scratchskip
+ % \ifodd\noflines
+ % \advance\noflines\plusone
+ % \fi
+ % \divide\noflines\rigidcolumns
+ %\else
+ \scratchdimen\ht\rigidcolumnbox
+ \divide\scratchdimen \rigidcolumns
+ \getnoflines\scratchdimen
+ %\fi
+ \else
+ \noflines\rigidcolumnlines % to be sure
+ \fi
+ \scratchdimen\noflines\lineheight
+ % new: we now loop so that we don't loose content
+ % since in practice we also use this macro for
+ % funny lineheights and border cases
+ \setbox0=\box\rigidcolumnbox
+ \doloop
+ {\setbox\rigidcolumnbox=\copy0
+ \setbox\scratchbox\normalhbox to \savedrigidhsize
+ {\dorecurse\rigidcolumns
+ {\setbox\scratchbox\vsplit\rigidcolumnbox to \scratchdimen
+ \dp\scratchbox\openstrutdepth
+ \setbox\scratchbox\normalvtop
+ \ifalignrigidcolumns to
+ \ifstretchrigidcolumns\vsize\else\scratchdimen\fi
+ \fi
+ {\unvbox\scratchbox}%
+ \wd\scratchbox\hsize
+ \box\scratchbox
+ \hfill}%
+ \hfillneg}%
+ \ifvoid\rigidcolumnbox\exitloop\else\advance\scratchdimen\lineheight\fi}%
+ \iftightrigidcolumns
+ \setbox\scratchbox\normalhbox{\raise\dp\scratchbox\box\scratchbox}%
+ \else
+ \advance\scratchdimen -\openstrutdepth
+ \setbox\scratchbox\normalhbox{\raise\scratchdimen\box\scratchbox}%
+ \dp\scratchbox\openstrutdepth
+ \ht\scratchbox\scratchdimen
+ \fi
+ \box\scratchbox}%
+ \fi}
+
+%D \macros
+%D {startvboxtohbox,stopvboxtohbox,convertvboxtohbox}
+%D
+%D Here is another of Knuth's dirty tricks, as presented on
+%D pages 398 and 399 of the \TEX book. These macros can be used
+%D like:
+%D
+%D \starttyping
+%D \normalvbox
+%D \bgroup
+%D \startvboxtohbox ... \stopvboxtohbox
+%D \startvboxtohbox ... \stopvboxtohbox
+%D \startvboxtohbox ... \stopvboxtohbox
+%D \egroup
+%D
+%D \normalvbox
+%D \bgroup
+%D \convertvboxtohbox
+%D \egroup
+%D \stoptyping
+%D
+%D These macros are used in reformatting footnotes, so they do
+%D what they're meant for.
+
+\def\setvboxtohbox
+ {\bgroup
+ \ifdim\baselineskip<16pt \relax
+ \scratchdimen\baselineskip
+ \multiply\scratchdimen 1024
+ \else
+ \message{cropping \baselineskip to 16pt}%
+ \scratchdimen\maxdimen
+ \fi
+ \divide\scratchdimen \hsize
+ \multiply\scratchdimen 64
+ \xdef\normalvboxtohboxfactor{\withoutpt\the\scratchdimen}%
+ \egroup}
+
+\def\startvboxtohbox
+ {\bgroup
+ \setvboxtohbox
+ \setbox\scratchbox\normalhbox\bgroup}
+
+\def\stopvboxtohbox
+ {\egroup
+ \dp\scratchbox\zeropoint
+ \ht\scratchbox\normalvboxtohboxfactor\wd\scratchbox
+ \box\scratchbox
+ \egroup}
+
+% % to be done: start halfway a line combined with one line
+% % extra to start with (skip) and one line less than counted.
+%
+% \def\stopvboxtohbox%
+% {\egroup
+% \setbox2=\normalvbox
+% {\forgetall\unhcopy0\par\xdef\globalvhlines{\the\prevgraf}}%
+% \setbox2=\normalvbox
+% {\unvbox2
+% \setbox2=\lastbox
+% \setbox2=\normalhbox{\unhbox2}%
+% \xdef\globalvhwidth{\the\wd2}}%
+% \decrement\globalvhlines
+% \dimen0=\globalvhwidth
+% \dimen0=\normalvboxtohboxfactor\dimen0
+% \advance\dimen0 by \globalvhlines\lineheight
+% \dp0=\zeropoint
+% \ht0=\dimen0
+% %\writestatus{guessed size}
+% % {w:\the\wd0\space\space
+% % b:\the\baselineskip\space
+% % l:\globalvhlines\space
+% % e:\globalvhwidth\space
+% % h:\the\dimen0}%
+% \box0
+% \egroup}
+
+% todo: \scratchbox
+
+\def\convertvboxtohbox
+ {\setvboxtohbox
+ \makehboxofhboxes
+ \setbox0\normalhbox{\unhbox0 \removehboxes}%
+ \noindent\unhbox0\par}
+
+\def\makehboxofhboxes
+ {\setbox0\normalhbox{}%
+ \loop % \doloop { .. \exitloop .. }
+ \setbox2\lastbox
+ \ifhbox2
+ \setbox0\normalhbox{\box2\unhbox0}%
+ \repeat}
+
+% \def\makehboxofhboxes
+% {\setbox0\normalhbox{}%
+% \doloop % \doloop { .. \exitloop .. }
+% {% \dorecurse{3}{\unskip\unpenalty}% get rid of ... (better do this in a shapeloop)
+% \setbox2\lastbox
+% \ifhbox2
+% \setbox0\normalhbox{\box2\unhbox0}%
+% \else
+% \exitloop
+% \fi}}
+
+% \def\flushboxesonly % feed this into \makehboxofhboxes
+% {\dowithnextbox
+% {\beginofshapebox
+% \unvbox\nextbox
+% \endofshapebox
+% \doreshapebox{\box\shapebox}{}{}{}% get rid of penalties etc
+% \innerflushshapebox}
+% \vbox}
+
+\def\removehboxes
+ {\setbox0\lastbox
+ \ifhbox0
+ {\removehboxes}\unhbox0
+ \fi}
+
+%D \macros
+%D {unhhbox}
+%D
+%D The next macro is used in typesetting inline headings.
+%D Let's first look at the macro and then show an example.
+
+\newbox \unhhedbox
+\newbox \hhbox
+\newdimen \lasthhboxwidth
+\newskip \hhboxindent
+
+\def\unhhbox#1\with#2%
+ {\bgroup
+ \nopenalties
+ \dontcomplain
+ \forgetall
+ \setbox\unhhedbox\normalvbox{\hskip\hhboxindent\strut\unhbox#1}% => \hsize
+ \doloop
+ {\setbox\hhbox\vsplit\unhhedbox to \lineheight
+ \ifvoid\unhhedbox
+ \setbox\hhbox\normalhbox{\strut\normalhboxofvbox\hhbox}%
+ \fi
+ \ht\hhbox\strutht
+ \dp\hhbox\strutdp
+ \ifzeropt\hhboxindent\else % \ifdim\hhboxindent=\zeropoint\else
+ \setbox\hhbox\normalhbox{\hskip-\hhboxindent\box\hhbox}%
+ \hhboxindent\zeropoint
+ \fi
+ \global\lasthhboxwidth\wd\hhbox
+ #2\relax
+ \ifvoid\unhhedbox
+ \exitloop
+ \else
+ \hskip\zeropoint \!!plus \zeropoint
+ \fi}%
+ \egroup}
+
+\def\dohboxofvbox
+ {\setbox0\normalvbox{\unvbox\scratchcounter\global\setbox1\lastbox}%
+ \unhbox1
+ \egroup}
+
+\def\normalhboxofvbox
+ {\bgroup
+ \afterassignment\dohboxofvbox
+ \scratchcounter=}
+
+%D This macro can be used to break a paragraph apart and treat
+%D each line seperately, for instance, making it clickable. The
+%D main complication is that we want to be able to continue the
+%D paragraph, something that's needed in the in line section
+%D headers.
+%D
+%D \startbuffer
+%D \setbox0=\normalhbox{\input tufte \relax}
+%D \setbox2=\normalhbox{\input knuth \relax}
+%D \unhhbox0\with{\ruledhbox{\box\hhbox}}
+%D \hskip1em plus 1em minus 1em
+%D \hhboxindent=\lasthhboxwidth
+%D \advance\hhboxindent by \lastskip
+%D \unhhbox2\with{\ruledhbox{\box\hhbox}}
+%D \stopbuffer
+%D
+%D \getbuffer
+%D
+%D This piece of text was typeset by saying:
+%D
+%D \typebuffer
+%D
+%D Not that nice a definition, but effective. Note the stretch
+%D we've build in the line that connects the two paragraphs.
+
+%D \macros
+%D {doifcontent}
+%D
+%D When processing depends on the availability of content, one
+%D can give the next macro a try.
+%D
+%D \starttyping
+%D \doifcontent{pre content}{post content}{no content}\somebox
+%D \stoptyping
+%D
+%D Where \type{\somebox} is either a \type{\normalhbox} or
+%D \type{\normalvbox}. If the dimension of this box suggest some
+%D content, the resulting box is unboxed and surrounded by the
+%D first two arguments, else the third arguments is executed.
+
+\unexpanded\def\doifcontent#1#2#3%
+ {\dowithnextbox
+ {\ifhbox\nextbox
+ \ifdim\nextboxwd>\zeropoint
+ #1\unhbox\nextbox#2\relax
+ \else
+ #3\relax
+ \fi
+ \else
+ \ifdim\nextboxht>\zeropoint
+ #1\unvbox\nextbox#2\relax
+ \else
+ #3\relax
+ \fi
+ \fi}}
+
+%D So when we say:
+%D
+%D \startbuffer
+%D \doifcontent{[}{]}{}\normalhbox{content sensitive typesetting}
+%D
+%D \doifcontent{}{\page}{}\normalvbox{content sensitive typesetting}
+%D
+%D \doifcontent{}{}{\message{Didn't you forget something?}}\normalhbox{}
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D We get:
+%D
+%D \getbuffer
+%D
+%D Where the last call of course does not show up in this
+%D document, but definitely generates a confusing message.
+
+%D \macros
+%D {processboxes}
+%D
+%D The next macro gobble boxes and is for instance used for
+%D overlays. First we show the general handler.
+
+\newbox\processbox
+
+\def\processboxes#1%
+ {\bgroup
+ \def\doprocessbox{#1}% #1 can be redefined halfway
+ \resetbox\processbox
+ \afterassignment\dogetprocessbox\let\next=}
+
+\def\endprocessboxes
+ {\ifhmode\unskip\fi
+ \box\processbox
+ \next
+ \egroup}
+
+\def\dogetprocessbox
+ {\ifx\next\bgroup
+ \expandafter\dodogetprocessbox
+ \else
+ \expandafter\endprocessboxes
+ \fi}
+
+\def\dodogetprocessbox
+ {\dowithnextbox
+ {\ifhmode\unskip\fi\doprocessbox % takes \nextbox makes \processbox
+ \afterassignment\dogetprocessbox\let\next=}
+ \normalhbox\bgroup}
+
+%D \macros
+%D {startoverlay}
+%D
+%D We can overlay boxes by saying:
+%D
+%D \startbuffer
+%D \startoverlay
+%D {\framed{hans}}
+%D {\framed[width=3cm]{ton}}
+%D {\framed[height=2cm]{oeps}}
+%D \stopoverlay
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D shows up as:
+%D
+%D \leavevmode\getbuffer
+
+% \def\dooverlaybox%
+% {\ifhmode\unskip\fi
+% \ifdim\nextboxht>\ht\processbox
+% \setbox\processbox\normalvbox to \nextboxht
+% {\vss\box\processbox\vss}%
+% \else
+% \setbox\nextbox\normalvbox to \ht\processbox
+% {\vss\flushnextbox\vss}%
+% \fi
+% \scratchdimen=\wd
+% \ifdim\nextboxwd>\wd\processbox
+% \nextbox
+% \else
+% \processbox
+% \fi
+% \setbox\processbox=\normalhbox to \scratchdimen
+% {\normalhbox to \scratchdimen{\hss\box\processbox\hss}%
+% \hskip-\scratchdimen
+% \normalhbox to \scratchdimen{\hss\flushnextbox\hss}}}
+%
+% \def\startoverlay%
+% {\bgroup
+% \let\stopoverlay\egroup
+% \processboxes\dooverlaybox}
+
+\def\dooverlaybox
+ {\ifhmode\unskip\fi
+ \scratchdimen\dp
+ \ifdim\nextboxdp>\dp\processbox
+ \nextbox
+ \else
+ \processbox
+ \fi
+ \ifdim\nextboxht>\ht\processbox
+ \setbox\processbox\normalvbox to \nextboxht
+ {\dp\processbox\zeropoint\vss\box\processbox\vss}%
+ \else
+ \setbox\nextbox\normalvbox to \ht\processbox
+ {\nextboxdp\zeropoint\vss\flushnextbox\vss}%
+ \fi
+ \nextboxdp\scratchdimen
+ \dp\processbox\scratchdimen
+ \scratchdimen\wd
+ \ifdim\nextboxwd>\wd\processbox
+ \nextbox
+ \else
+ \processbox
+ \fi
+ \setbox\processbox\normalhbox to \scratchdimen
+ {\normalhbox to \scratchdimen{\hss\box\processbox\hss}%
+ \hskip-\scratchdimen
+ \normalhbox to \scratchdimen{\hss\flushnextbox\hss}}}
+
+\unexpanded\def\startoverlay
+ {\bgroup
+ \let\stopoverlay\egroup
+ \processboxes\dooverlaybox}
+
+\let\stopoverlay\relax
+
+% %D \macros
+% %D {starthspread}
+% %D
+% %D In a similar way we can build a horizontal box, spread
+% %D over the available width.
+% %D
+% %D \startbuffer
+% %D \starthspread
+% %D {hans}
+% %D {ton}
+% %D {oeps}
+% %D \stophspread
+% %D
+% %D \stopbuffer
+% %D
+% %D \typebuffer
+% %D
+% %D shows up as:
+% %D
+% %D \leavevmode\getbuffer
+%
+% \def\dohspread
+% {\flushnextbox
+% \def\dohspread{\hfil\flushnextbox}}
+%
+% \def\starthspread
+% {\normalhbox to \hsize \bgroup
+% \let\stophspread\egroup
+% \processboxes\dohspread}
+
+%D \macros
+%D {fakebox}
+%D
+%D The next macro is a rather silly one, but saves space.
+%D
+%D \starttyping
+%D \normalhbox{\fakebox0}
+%D \stoptyping
+%D
+%D returns an empty box with the dimensions of the box
+%D specified, here being zero.
+
+\def\dofakebox
+ {\setbox\scratchbox\null
+ \wd\scratchbox\wd\scratchcounter
+ \ht\scratchbox\ht\scratchcounter
+ \dp\scratchbox\dp\scratchcounter
+ \ifhbox\scratchcounter\normalhbox\else\normalvbox\fi{\box\scratchbox}%
+ \egroup}
+
+\def\fakebox
+ {\bgroup
+ \afterassignment\dofakebox\scratchcounter}
+
+%D \macros
+%D {lbox,rbox,cbox,tbox,bbox}
+%D
+%D Here are some convenient alternative box types:
+%D
+%D \starttyping
+%D \lbox{text ...}
+%D \cbox{text ...}
+%D \rbox{text ...}
+%D \stoptyping
+%D
+%D Are similar to \type {\normalvbox}, which means that they also
+%D accept something like \type{to 3cm}, but align to the left,
+%D middle and right. These box types can be used to typeset
+%D paragraphs.
+
+\def\lbox{\makelrcbox\normalvbox\raggedleft}
+\def\cbox{\makelrcbox\normalvbox\raggedcenter}
+\def\rbox{\makelrcbox\normalvbox\raggedright}
+
+\def\ltop{\makelrcbox\normalvtop\raggedleft}
+\def\ctop{\makelrcbox\normalvtop\raggedcenter}
+\def\rtop{\makelrcbox\normalvtop\raggedright}
+
+\def\makelrcbox#1#2#3#%
+ {#1#3\bgroup \forgetall \let\\=\endgraf #2\let\next=}
+
+%D The alternatives \type {\tbox} and \type {\bbox} can be used
+%D to properly align boxes, like in:
+%D
+%D \setupexternalfigures[directory={../sample}]
+%D \startbuffer
+%D \starttable[|||]
+%D \HL
+%D \VL \tbox{\externalfigure[cow][height=3cm,frame=on]} \VL top aligned \VL\SR
+%D \HL
+%D \VL \bbox{\externalfigure[cow][height=3cm,frame=on]} \VL bottom aligned \VL\SR
+%D \HL
+%D \stoptable
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D The positioning depends on the strut settings:
+%D
+%D \getbuffer
+
+\def\tbox{\tbbox\ht\dp}
+\def\bbox{\tbbox\dp\ht}
+
+\def\tbbox#1#2%
+ {\normalhbox\bgroup
+ \dowithnextbox
+ {\scratchdimen\nextboxht
+ \advance\scratchdimen\nextboxdp
+ \advance\scratchdimen-#1\strutbox
+ #1\nextbox#1\strutbox
+ #2\nextbox\scratchdimen
+ \setbox\nextbox\normalhbox
+ {\lower\nextboxdp\flushnextbox}%
+ #1\nextbox#1\strutbox
+ #2\nextbox\scratchdimen
+ \flushnextbox
+ \egroup}
+ \normalhbox}
+
+%D \macros
+%D {lhbox,mhbox,rhbox}
+%D
+%D A few more boxes.
+
+\def\dodolhbox{\normalhbox to \hsize{\flushnextbox\hss }}
+\def\dodomhbox{\normalhbox to \hsize{\hss\flushnextbox\hss}}
+\def\dodorhbox{\normalhbox to \hsize{\hss\flushnextbox }}
+
+\def\lhbox{\dowithnextboxcs\dodolhbox\normalhbox}
+\def\mhbox{\dowithnextboxcs\dodomhbox\normalhbox}
+\def\rhbox{\dowithnextboxcs\dodorhbox\normalhbox}
+
+\let\lefthbox \lhbox
+\let\midhbox \mhbox
+\let\righthbox\rhbox
+
+%D \macros
+%D {boxofsize}
+%D
+%D Sometimes we need to construct a box with a height or
+%D width made up of several dimensions. Instead of cumbersome
+%D additions, we can use:
+%D
+%D \starttyping
+%D \boxofsize \normalvbox 10cm 3cm -5cm {the text to be typeset}
+%D \stoptyping
+%D
+%D This example demonstrates that one can use positive and
+%D negative values. Dimension registers are also accepted.
+
+\newdimen\sizeofbox
+
+\def\boxofsize#1%
+ {\bgroup
+ \sizeofbox\zeropoint
+ \scratchdimen\zeropoint
+ \def\docommand
+ {\advance\sizeofbox\scratchdimen
+ \futurelet\next\dodocommand}%
+ \def\dodocommand
+ {\ifx\next\bgroup
+ \expanded{\egroup#1 to \the\sizeofbox}%
+ \else
+ \@EA\afterassignment\@EA\docommand\@EA\scratchdimen
+ \fi}%
+ \docommand}
+
+%D Some new, still undocumented features:
+
+% limitatetext -> beter {text} als laatste !!
+%
+% \limitvbox
+% \limithbox
+
+\def\limitatelines#1#2% size sentinel
+ {\dowithnextbox
+ {\dimen0=#1\hsize
+ \ifdim\nextboxwd>\dimen0
+ \setbox\nextbox\normalhbox
+ {\advance\dimen0 -.1\hsize
+ \limitatetext{\unhbox\nextbox}{\dimen0}{\nobreak#2}}%
+ \fi
+ \unhbox\nextbox}
+ \normalhbox}
+
+\def\fittoptobaselinegrid % weg hier
+ {\dowithnextbox
+ {\bgroup
+ \par
+ \dimen0\nextboxht
+ \nextboxht\strutht
+ \nextboxdp\strutdp
+ \normalhbox{\flushnextbox}
+ \prevdepth\strutdp
+ \doloop
+ {\advance\dimen0 -\lineheight
+ \ifdim\dimen0<\zeropoint
+ \exitloop
+ \else
+ \nobreak
+ \normalhbox{\strut}
+ \fi}
+ \egroup}
+ \normalvbox}
+
+%D Some more undocumented macros (used in m-chart).
+
+\newif\iftraceboxplacement % \traceboxplacementtrue
+
+\newbox\fakedboxcursor
+
+\setbox\fakedboxcursor\normalhbox
+ {\vrule\!!width\zeropoint\!!height\zeropoint\!!depth\zeropoint}
+
+\def\boxcursor % overloaded in core-vis
+ {\iftraceboxplacement
+ \bgroup
+ \scratchdimen2pt
+ \setbox\scratchbox\normalhbox to \zeropoint
+ {\hss
+ \vrule
+ \!!width \scratchdimen
+ \!!height\scratchdimen
+ \!!depth \scratchdimen
+ \hss}%
+ \smashedbox\scratchbox
+ \egroup
+ \else
+ \copy\fakedboxcursor
+ \fi}
+
+\def\placedbox
+ {\iftraceboxplacement\ruledhbox\else\normalhbox\fi}
+
+\newdimen\boxoffset
+\newdimen\boxhdisplacement
+\newdimen\boxvdisplacement
+
+%\def\rightbox#1%
+% {\normalhbox
+% {\setbox0=\placedbox{#1}%
+% \dimen0=.5\ht0\advance\dimen0 -.5\dp0
+% \boxcursor\hskip\boxoffset\lower\dimen0\box0}}
+
+\def\rightbox#1%
+ {\normalhbox
+ {\setbox0\placedbox{#1}%
+ \global\boxhdisplacement\boxoffset
+ \global\boxvdisplacement.5\ht0
+ \global\advance\boxvdisplacement-.5\dp0
+ \boxcursor\hskip\boxhdisplacement\lower\boxvdisplacement\box0}}
+
+%\def\leftbox#1%
+% {\normalhbox
+% {\setbox0=\placedbox{#1}%
+% \dimen0=.5\ht0\advance\dimen0 -.5\dp0
+% \boxcursor\hskip-\wd0\hskip-\boxoffset\lower\dimen0\box0}}
+
+\def\leftbox#1%
+ {\normalhbox
+ {\setbox0\placedbox{#1}%
+ \global\boxhdisplacement-\wd0
+ \global\advance\boxhdisplacement-\boxoffset
+ \global\boxvdisplacement.5\ht0
+ \global\advance\boxvdisplacement-.5\dp0
+ \boxcursor\hskip\boxhdisplacement\lower\boxvdisplacement\box0}}
+
+%\def\topbox#1%
+% {\normalhbox
+% {\setbox0=\placedbox{#1}%
+% \dimen0=\boxoffset\advance\dimen0 \dp0
+% \boxcursor\hskip-.5\wd0\raise\dimen0\box0}}
+
+\def\topbox#1%
+ {\normalhbox
+ {\setbox0\placedbox{#1}%
+ \global\boxhdisplacement-.5\wd0
+ \global\boxvdisplacement-\dp0
+ \global\advance\boxvdisplacement-\boxoffset
+ \boxcursor\hskip\boxhdisplacement\raise-\boxvdisplacement\box0}}
+
+%\def\bottombox#1%
+% {\normalhbox
+% {\setbox0=\placedbox{#1}%
+% \dimen0=\boxoffset\advance\dimen0 \ht0
+% \boxcursor\hskip-.5\wd0\lower\dimen0\box0}}
+
+\def\bottombox#1%
+ {\normalhbox
+ {\setbox0\placedbox{#1}%
+ \global\boxhdisplacement-.5\wd0
+ \global\boxvdisplacement\ht0
+ \global\advance\boxvdisplacement\boxoffset
+ \boxcursor\hskip\boxhdisplacement\lower\boxvdisplacement\box0}}
+
+%\def\lefttopbox#1%
+% {\normalhbox
+% {\setbox0=\placedbox{#1}%
+% \dimen0=\boxoffset\advance\dimen0 \dp0
+% \advance\boxoffset\wd0
+% \boxcursor\hskip-\boxoffset\raise\dimen0\box0}}
+
+\def\lefttopbox#1%
+ {\normalhbox
+ {\setbox0\placedbox{#1}%
+ \global\boxhdisplacement-\wd0
+ \global\advance\boxhdisplacement-\boxoffset
+ \global\boxvdisplacement-\dp0
+ \global\advance\boxvdisplacement-\boxoffset
+ \boxcursor\hskip\boxhdisplacement\raise-\boxvdisplacement\box0}}
+
+%\def\righttopbox#1%
+% {\normalhbox
+% {\setbox0=\placedbox{#1}%
+% \dimen0=\boxoffset\advance\dimen0 \dp0
+% \boxcursor\hskip\boxoffset\raise\dimen0\box0}}
+
+\def\righttopbox#1%
+ {\normalhbox
+ {\setbox0\placedbox{#1}%
+ \global\boxhdisplacement\boxoffset
+ \global\boxvdisplacement-\dp0
+ \global\advance\boxvdisplacement-\boxoffset
+ \boxcursor\hskip\boxhdisplacement\raise-\boxvdisplacement\box0}}
+
+%\def\leftbottombox#1%
+% {\normalhbox
+% {\setbox0=\placedbox{#1}%
+% \dimen0=\boxoffset\advance\dimen0 \ht0
+% \advance\boxoffset\wd0
+% \boxcursor\hskip-\boxoffset\lower\dimen0\box0}}
+
+\def\leftbottombox#1%
+ {\normalhbox
+ {\setbox0\placedbox{#1}%
+ \global\boxhdisplacement-\wd0
+ \global\advance\boxhdisplacement-\boxoffset
+ \global\boxvdisplacement\ht0
+ \global\advance\boxvdisplacement\boxoffset
+ \boxcursor\hskip\boxhdisplacement\lower\boxvdisplacement\box0}}
+
+%\def\rightbottombox#1%
+% {\normalhbox
+% {\setbox0=\placedbox{#1}%
+% \dimen0=\boxoffset\advance\dimen0 \ht0
+% \boxcursor\hskip\boxoffset\lower\dimen0\box0}}
+
+\def\rightbottombox#1%
+ {\normalhbox
+ {\setbox0\placedbox{#1}%
+ \global\boxhdisplacement\boxoffset
+ \global\boxvdisplacement\ht0
+ \global\advance\boxvdisplacement\boxoffset
+ \boxcursor\hskip\boxhdisplacement\lower\boxvdisplacement\box0}}
+
+\let\topleftbox \lefttopbox
+\let\toprightbox \righttopbox
+\let\bottomleftbox \leftbottombox
+\let\bottomrightbox\rightbottombox
+
+\def\middlebox#1%
+ {\normalhbox{\setbox0\placedbox{#1}\boxoffset=-.5\wd0\rightbox{\box0}}}
+
+\def\baselinemiddlebox#1%
+ {\normalhbox
+ {\setbox0\placedbox{#1}%
+ \global\boxhdisplacement-.5\wd0
+ \global\advance\boxhdisplacement-\boxoffset
+ \global\boxvdisplacement-\boxoffset
+ \boxcursor\hskip\boxhdisplacement\raise-\boxvdisplacement\box0}}
+
+\def\baselineleftbox#1%
+ {\normalhbox
+ {\setbox0\placedbox{#1}%
+ \global\boxhdisplacement-\wd0
+ \global\advance\boxhdisplacement-\boxoffset
+ \global\boxvdisplacement-\boxoffset
+ \boxcursor\hskip\boxhdisplacement\raise-\boxvdisplacement\box0}}
+
+\def\baselinerightbox#1%
+ {\normalhbox
+ {\setbox0\placedbox{#1}%
+ \global\boxhdisplacement\boxoffset
+ \global\boxvdisplacement-\boxoffset
+ \boxcursor\hskip\boxhdisplacement\raise-\boxvdisplacement\box0}}
+
+%D \macros
+%D {obox}
+%D
+%D Experimental, not yet frozen:
+
+\def\lrtbbox#1#2#3#4% l r t b
+ {\bgroup
+ \dowithnextboxcontent
+ {\advance\hsize-#1\advance\hsize-#2\advance\vsize-#3\advance\vsize-#4\relax}
+ {\forgetall\vbox to \vsize{\vskip#3\hbox to \hsize{\hskip#1\box\nextbox\hss}\vss}\egroup}
+ \vbox}
+
+%D \macros
+%D {toplinebox}
+%D
+%D See core-tbl.tex for an example of its usage:
+
+\def\toplinebox
+ {\dowithnextbox
+ {\ifdim\nextboxdp>\strutdepth
+ \scratchdimen\nextboxdp
+ \advance\scratchdimen-\strutdepth
+ \getnoflines\scratchdimen
+ \struttedbox{\flushnextbox}%
+ \dorecurse\noflines\verticalstrut
+ \else
+ \flushnextbox
+ \fi}%
+ \tbox}
+
+%D \macros
+%D {initializeboxstack,savebox,foundbox}
+%D
+%D At the cost of some memory, but saving box registers, we
+%D have implemented a box repository.
+%D
+%D \starttyping
+%D \initializeboxstack{one}
+%D
+%D \savebox{one}{a}{test a}
+%D \savebox{one}{p}{test p}
+%D \savebox{one}{q}{test q}
+%D
+%D \normalhbox{a:\foundbox{one}{a}} \par
+%D \normalhbox{q:\foundbox{one}{q}} \par
+%D \normalhbox{p:\foundbox{one}{p}} \par
+%D \normalhbox{x:\foundbox{one}{x}} \par
+%D \normalhbox{y:\foundbox{two}{a}} \par
+%D \stoptyping
+
+% we keep it around as a demonstration of good old tex code:
+%
+% \def\@@stackbox{boxstack:b:}
+% \def\@@stackmax{boxstack:m:}
+% \def\@@stacktag{boxstack:t:}
+% \def\@@stacklst{boxstack:l:}
+%
+% \def\initializeboxstack#1%
+% {\ifundefined{\@@stackbox#1}%
+% \@EA\newbox\csname\@@stackbox#1\endcsname
+% \else
+% \global\setbox\csname\@@stackbox#1\endcsname\normalvbox{}%
+% \def\docommand##1{\global\letbeundefined{\@@stacktag#1:##1}}%
+% \processcommacommand[\getvalue{\@@stacklst#1}]\docommand
+% \fi
+% \global\letvalue{\@@stacklst#1}\empty
+% \global\letvalue{\@@stackmax#1}\!!zeropoint}
+%
+% \def\savebox#1#2% stack name
+% {\dowithnextbox
+% {\doifdefined{\@@stackbox#1}
+% {\@EA\doglobal\@EA\increment\csname\@@stackmax#1\endcsname
+% \setxvalue{\@@stacktag#1:#2}{\csname\@@stackmax#1\endcsname}%
+% \setxvalue{\@@stacklst#1}{\getvalue{\@@stacklst#1},#2}%
+% \global\setbox\csname\@@stackbox#1\endcsname\normalvbox
+% {\forgetall
+% \setbox\scratchbox\normalvbox{\flushnextbox}
+% \ht\scratchbox\onepoint
+% \dp\scratchbox\zeropoint
+% \unvbox\csname\@@stackbox#1\endcsname
+% \offinterlineskip
+% \allowbreak
+% \box\scratchbox}}}%
+% \normalvbox}
+%
+% \def\foundbox#1#2%
+% {\normalvbox
+% {\doifdefined{\@@stackbox#1}
+% {\doifdefined{\@@stacktag#1:#2}
+% {\setbox\scratchbox\normalvbox
+% {\splittopskip\zeropoint
+% \setbox0\copy\csname\@@stackbox#1\endcsname
+% \dimen0=\getvalue{\@@stacktag#1:#2}\points
+% \advance\dimen0 -\onepoint
+% \setbox2\vsplit0 to \dimen0
+% \ifdim\ht0>\onepoint
+% \setbox0\vsplit0 to \onepoint
+% \fi
+% \unvbox0\setbox0\lastbox\unvbox0}%
+% \unvbox\scratchbox}}}}
+%
+% \def\doifboxelse#1#2%
+% {\doifdefinedelse{\@@stacktag#1:#2}}
+
+\def\@@stackbox{@box@}
+\def\@@stacklst{@xob@}
+
+\def\setstackbox#1#2%
+ {\ifcsname\@@stackbox:#1:#2\endcsname\else
+ \expandafter\newbox\csname\@@stackbox:#1:#2\endcsname
+ \fi
+ \global\setbox\csname\@@stackbox:#1:#2\endcsname\normalvbox}
+
+\def\initializeboxstack#1%
+ {\def\docommand##1{\setstackbox{#1}{##1}{}}%
+ \ifcsname\@@stacklst#1\endcsname
+ \processcommacommand[\getvalue{\@@stacklst#1}]\docommand
+ \fi
+ \global\letvalue{\@@stacklst#1}\empty}
+
+\def\savebox#1#2% stack name
+ {% beware, \setxvalue defines the cs beforehand so we cannot use the
+ % test inside the { }
+ \ifcsname\@@stacklst#1\endcsname
+ \setxvalue{\@@stacklst#1}{\csname\@@stacklst#1\endcsname,#2}%
+ \else
+ \setxvalue{\@@stacklst#1}{#2}%
+ \fi
+ \setstackbox{#1}{#2}}
+
+\def\foundbox#1#2%
+ {\normalvbox
+ {\ifcsname\@@stackbox:#1:#2\endcsname
+ \copy\csname\@@stackbox:#1:#2\endcsname
+ \fi}}
+
+\long\def\doifboxelse#1#2#3#4%
+ {\ifcsname\@@stackbox:#1:#2\endcsname
+ \ifvoid\csname\@@stackbox:#1:#2\endcsname#4\else#3\fi
+ \else
+ #4%
+ \fi}
+
+\endETEX
+
+%D \macros
+%D {removedepth, obeydepth}
+%D
+%D While \type {\removedepth} removes the preceding depth,
+%D \type {\obeydepth} makes sure we have depth. Both macros
+%D leave the \type {\prevdepth} untouched.
+
+\def\removedepth
+ {\ifvmode \ifdim\prevdepth>\zeropoint \kern-\prevdepth \fi \fi}
+
+\def\obeydepth
+ {\par \removedepth \ifvmode \kern\strutdp \fi}
+
+\def\undepthed
+ {\dowithnextbox{\nextboxdp\zeropoint\flushnextbox}\hbox}
+
+%D \macros
+%D {removebottomthings, removelastskip}
+%D
+%D A funny (but rather stupid) one, plus a redefinition.
+
+\def\removebottomthings
+ {\dorecurse5{\unskip\unkern\unpenalty}}
+
+\def\removelastskip % \ifvmode the plain tex one \fi
+% {\ifvmode\ifdim\lastskip=\zeropoint\else\vskip-\lastskip\fi\fi}
+ {\ifvmode\ifzeropt\lastskip\else\vskip-\lastskip\fi\fi}
+
+%D \macros
+%D {makestrutofbox}
+%D
+%D This macro sets the dimensions of a box to those of a
+%D strut.
+
+\def\domakestrutofbox
+ {\ht\registercount\strutht
+ \dp\registercount\strutdp
+ \wd\registercount\zeropoint}
+
+\def\makestrutofbox
+ {\afterassignment\domakestrutofbox\registercount}
+
+%D \macros
+%D {raisebox,lowerbox}
+%D
+%D Some more box stuff, related to positioning (under
+%D construction). Nice stuff for a tips and tricks maps
+%D article.
+%D
+%D \starttyping
+%D \raisebox{100pt}\normalhbox{test}
+%D \raisebox50pt\normalhbox{test}
+%D \hsmash{\raisebox{100pt}\normalhbox{test}}
+%D \stoptyping
+
+\def\doraiselowerbox#1#2% a nice trick us used to accept
+ {\def\next % both direct and {} dimensions
+ {\dowithnextbox
+ {\setbox\nextbox\normalhbox{#1\scratchdimen\flushnextbox}%
+ \nextboxht\strutht
+ \nextboxdp\strutdp
+ \flushnextbox}}%
+ \afterassignment\next\scratchdimen=#2}
+
+\def\raisebox{\doraiselowerbox\raise}
+\def\lowerbox{\doraiselowerbox\lower}
+
+% maybe some day we need this
+%
+% \def\appendvbox#1% % uses \box8
+% {\bgroup
+% \ifdim\prevdepth<\zeropoint
+% \ifdim\pagetotal=\zeropoint
+% \setbox8=\normalvtop{\unvcopy#1}%
+% \hrule\c!!height\zeropoint
+% \kern-\ht8
+% \box#1\relax
+% \else
+% \box#1\relax
+% \fi
+% \else
+% \dimen0=\prevdepth
+% \hrule\c!!height\zeropoint
+% \setbox8=\normalvtop{\unvcopy#1}%
+% \dimen2=\baselineskip
+% \advance\dimen2 by -\dimen0
+% \advance\dimen2 by -\ht8
+% \kern\dimen2
+% \box#1\relax
+% \fi
+% \egroup}
+
+% %D Also new:
+% %D
+% %D \startbuffer
+% %D \normbox[1cm][bba]{m} % b(efore) a(fter) v(box) s(trut) f(rame)
+% %D \normbox[1cm][bba]{m}
+% %D \normbox[1cm][bba]{m}
+% %D \stopbuffer
+% %D
+% %D \typebuffer
+% %D \getbuffer
+%
+% \def\dodonormbox#1#2#3#4#5#6#7%
+% {\doifnumberelse{#1}
+% {\dimen0=#1}{\setbox0=#3{#1}\dimen0=#50}%
+% \doifinstringelse{f}{#2}
+% {\let\next#4}{\let\next#3}%
+% \next to \dimen0
+% {\counttoken b\in#2\to\!!counta\dorecurse{\!!counta}{#6}#6%
+% #7\nextbox
+% \counttoken a\in#2\to\!!counta\dorecurse{\!!counta}{#6}#6}}
+%
+% \def\donormbox[#1][#2]%
+% {\bgroup
+% \doifinstringelse{v}{#2}
+% {\let\next\normalvbox}
+% {\let\next\normalhbox}%
+% \dowithnextbox
+% {\ifvbox\nextbox
+% \let\\=\par
+% \dodonormbox{#1}{#2}\normalvbox\ruledvbox\ht\vfil\unvbox
+% \else
+% \let\\=\space
+% \dodonormbox{#1}{#2}\normalhbox\ruledhbox\wd\hfil\unhbox
+% \fi
+% \egroup}%
+% \next}
+%
+% \def\normbox
+% {\dodoubleempty\donormbox}
+
+% vcenter in text, we kunnen vcenter overloaden
+
+\def\halfwaybox
+ {\dowithnextbox
+ {\nextboxdp\zeropoint
+ \setbox\nextbox\normalhbox{\lower.5\nextboxht\flushnextbox}%
+ \flushnextbox}
+ \normalhbox}
+
+%D New:
+
+\def\setdimentoatleast#1#2%
+ {\ifdim#1>\zeropoint\else#1=#2\fi}
+
+%D And even rawer:
+
+\let\naturalhbox \normalhbox
+\let\naturalvbox \normalvbox
+\let\naturalvtop \normalvtop
+\let\naturalvcenter \normalvtop
+
+\ifdefined\textdir
+
+ \def\naturalhbox{\normalhbox dir TLT}
+ \def\naturalvbox{\normalvbox dir TLT}
+ %def\naturalvtop{\normalvtop dir TLT}
+
+\fi
+
+%D \macros
+%D {vcenter}
+%D
+%D Also new: tex mode \type {\vcenter}.
+
+\let\verynormalvcenter \vcenter % since \vcenter can be visualized
+
+\def\vcenter
+ {\normalvbox\bgroup
+ \dowithnextbox{\normalhbox{$\verynormalvcenter{\flushnextbox}$}\egroup}
+ \normalvbox}
+
+% could be \everymathematics
+
+\prependtoks \let\vcenter\normalvcenter \to \everymath
+\prependtoks \let\vcenter\normalvcenter \to \everydisplay
+
+%D \macros
+%D {frozenhbox}
+%D
+%D A not so well unhboxable bxo can be made with:
+
+\def\frozenhbox
+ {\hbox\bgroup\dowithnextbox{\hbox{\hbox{\flushnextbox}}\egroup}\hbox}
+
+%D \macros
+%D {setboxllx,setboxlly,gsetboxllx,gsetboxlly,getboxllx,getboxlly}
+%D
+%D A prelude to an extended \TEX:
+
+% \def\setboxllx #1#2{\bgroup\scratchdimen#2\expanded{\egroup\noexpand\setevalue{b@@x\number#1}{\the\scratchdimen}}}
+% \def\setboxlly #1#2{\bgroup\scratchdimen#2\expanded{\egroup\noexpand\setevalue{b@@y\number#1}{\the\scratchdimen}}}
+%
+% \def\gsetboxllx#1#2{\bgroup\scratchdimen#2\setxvalue{b@@x\number#1}{\the\scratchdimen}\egroup}
+% \def\gsetboxlly#1#2{\bgroup\scratchdimen#2\setxvalue{b@@y\number#1}{\the\scratchdimen}\egroup}
+
+\def\setboxllx#1#2{\setevalue{b@@x\number#1}{\the\dimexpr#2\relax}}
+\def\setboxlly#1#2{\setevalue{b@@y\number#1}{\the\dimexpr#2\relax}}
+
+\def\gsetboxllx{\global\setboxllx}
+\def\gsetboxlly{\global\setboxlly}
+
+\def\getboxllx#1{\executeifdefined{b@@x\number#1}\zeropoint}
+\def\getboxlly#1{\executeifdefined{b@@y\number#1}\zeropoint}
+
+%D \macros
+%D {shownextbox}
+%D
+%D Handy for tracing
+%D
+%D \starttyping
+%D \shownextbox\vbox{test}
+%D \shownextbox\vbox{test\endgraf}
+%D \shownextbox\vbox{test\endgraf\strut\endgraf}
+%D \shownextbox\vbox{test\endgraf\thinrule}
+%D \shownextbox\vbox{\setupwhitespace[big]test\endgraf\thinrule}
+%D \stoptyping
+
+\def\shownextbox
+ {\dowithnextbox
+ {\bgroup
+ \showboxbreadth\maxdimen
+ \showboxdepth \maxdimen
+ \scratchcounter\interactionmode
+ \batchmode
+ \showbox\nextbox
+ \box\nextbox
+ \interactionmode\scratchcounter
+ \egroup}}
+
+\def\spreadhbox#1% rebuilds \hbox{<box><hss><box><hss><box>}
+ {\bgroup
+ \ifhbox#1\relax
+ \setbox2\emptybox
+ \unhbox#1%
+ \doloop
+ {\unpenalty\unskip\unpenalty\unskip\unpenalty\unskip
+ \setbox0\lastbox
+ \ifvoid0
+ \exitloop
+ \else
+ \setbox2\hbox
+ {\ifhbox0 \spreadhbox0\else\box0\fi
+ \ifvoid2 \else\hss\unhbox2\fi}%
+ \fi}%
+ \ifvoid2\else\unhbox2\fi
+ \else
+ \box#1%
+ \fi
+ \egroup}
+
+% makes sense but too much log for overfull boxes:
+%
+% \showboxbreadth\maxdimen
+% \showboxdepth \maxdimen
+
+\protect \endinput
+
+% a bit of test code:
+
+\hbox \bgroup
+ \ruledvbox {\hbox{\strut gans}}
+ \ruledvbox to \lineheight {\hbox{\strut gans}}
+ \ruledvbox to \lineheight {\hbox {gans}}
+ \ruledvbox to \strutheight{\hbox {gans}}
+ \ruledvbox to \strutheight{\hbox{\strut gans}}
+ \ruledvbox to \strutheight{\vss\hbox{gans}}
+\egroup