%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.

\writestatus{loading}{Context Support Macros / Boxes}

\unprotect

% watch this: \setbox4\emptybox \wd4\onepoint \the\wd4, no dimensions for void

%D First a couple of hacks to make this module loadable
%D in plain \TEX.

\ifx\myalloc@\undefined % seems like we're not in context
  \def\newbox{\alloc@4\box\chardef\insc@unt}
\fi

\ifx      \scratchbox\undefined \newbox      \scratchbox \fi
\ifx\globalscratchbox\undefined \newbox\globalscratchbox \fi

\ifx\normalhbox   \undefined \let\normalhbox   \hbox    \fi
\ifx\normalvbox   \undefined \let\normalvbox   \vbox    \fi
\ifx\normalvtop   \undefined \let\normalvtop   \vtop    \fi
\ifx\normalvcenter\undefined \let\normalvcenter\vcenter \fi

%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}.

\def\emptybox           {\box\voidb@x}
\def\resetbox#1{\setbox#1\box\voidb@x}

%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{$\m@th#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.

\def\phantom {\ph@nt\nextbox\nextbox\nextbox}
\def\vphantom{\ph@nt\nextbox\nextbox\voidb@x}
\def\hphantom{\ph@nt\voidb@x\voidb@x\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{$\m@th####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 % why no \bgroup
   \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}.

\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\ht\registercount\advance#1\dp\registercount}%
   \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#2>\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}

%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
  {\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{\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}

%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\dorlap{\begingroup\dowithnextbox{\normalhbox to \zeropoint
   {\flushnextbox\normalhss}\endgroup}\normalhbox}

\def\dollap{\begingroup\dowithnextbox{\normalhbox to \zeropoint
   {\normalhss\flushnextbox}\endgroup}\normalhbox}

\def\doclap{\begingroup\dowithnextbox{\normalhbox to \zeropoint
   {\normalhss\flushnextbox\normalhss}\endgroup}\normalhbox}

\def\domathclap{\mathpalette\dodomathclap} \def\dodomathclap#1#2{\doclap{$\m@th#1#2$}}
\def\domathllap{\mathpalette\dodomathllap} \def\dodomathllap#1#2{\dollap{$\m@th#1#2$}}
\def\domathrlap{\mathpalette\dodomathrlap} \def\dodomathrlap#1#2{\dorlap{$\m@th#1#2$}}

\unexpanded\def\rlap{\mathortext\domathrlap\dorlap}
\unexpanded\def\llap{\mathortext\domathllap\dollap}
\unexpanded\def\clap{\mathortext\domathclap\doclap}

\def\tlap{\begingroup\dowithnextbox{\normalvbox to \zeropoint
  {\normalvss\flushnextbox}\endgroup}\normalvbox}

\def\blap{\begingroup\dowithnextbox{\normalvbox to \zeropoint
  {\flushnextbox\normalvss}\endgroup}\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

\beginTEX

\def\insertshapesignal
  {\normalhbox to \shapesignal{\strut\hss}% plus \strut
   \prevdepth\strutdp} % never \nointerlineskip

% \def\restoreshapebox
%   {\global\setbox\tmpshapebox\copy\originalshapebox} % \oldshapebox

\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}

\endTEX

% Now that the lastnode bugfixes are wide spread we can use:

\beginETEX \lastnodetype

\def\insertshapesignal
  {\normalhbox to \shapesignal{\strut\hss}% plus \strut
   \prevdepth\strutdp} % never \nointerlineskip

% \def\restoreshapebox
%   {\global\setbox\tmpshapebox\copy\originalshapebox} % \oldshapebox

\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}

\endETEX

\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.
%D
%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 ! ! ! ! ! ! ! ! !
   \dowithnextbox\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}

\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}}}

\def\startoverlay
  {\bgroup
   \let\stopoverlay\egroup
   \processboxes\dooverlaybox}

% %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\lhbox{\dowithnextbox{\normalhbox to \hsize{\flushnextbox\hss    }}\normalhbox}
\def\mhbox{\dowithnextbox{\normalhbox to \hsize{\hss\flushnextbox\hss}}\normalhbox}
\def\rhbox{\dowithnextbox{\normalhbox to \hsize{\hss\flushnextbox    }}\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

% a first version
%
% \def\@@stackbox{boxstack:b:}
% \def\@@stackmax{boxstack:m:}
% \def\@@stacktag{boxstack:t:}
%
% \def\initializeboxstack#1%
%   {\ifundefined{\@@stackbox#1}%
%      \@EA\newbox\csname\@@stackbox#1\endcsname
%    \else
%      \global\setbox\csname\@@stackbox#1\endcsname\normalvbox{}%
%    \fi
%    % actually we should erase the old values
%    \setgvalue{\@@stackmax#1}{0}}
%
% \def\savebox#1#2% stack name
%   {\dowithnextbox
%      {\doifdefined{\@@stackbox#1}
%         {\@EA\doglobal\@EA\increment\csname\@@stackmax#1\endcsname
%          \setxvalue{\@@stacktag#2}{\csname\@@stackmax#1\endcsname}%
%          \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#2}
%            {\setbox\scratchbox=\normalvbox
%               {\splittopskip\zeropoint
%                \setbox0=\copy\csname\@@stackbox#1\endcsname
%                \dimen0=\getvalue{\@@stacktag#2}pt
%                \advance\dimen0 by -\onepoint
%                \setbox2=\vsplit0 to \dimen0
%                \ifdim\ht0>\onepoint
%                  \setbox0=\vsplit0 to \onepoint
%                \fi
%                \unvbox0\setbox0=\lastbox\unvbox0}%
%             \unvbox\scratchbox}}}}

\beginTEX \newbox

\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}}

\endTEX

\beginETEX \newbox

\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\raise}

% 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 We need'm raw.

\ifx \normalhbox    \undefined \let \normalhbox    = \hbox    \fi
\ifx \normalvbox    \undefined \let \normalvbox    = \vbox    \fi
\ifx \normalvtop    \undefined \let \normalvtop    = \vtop    \fi
\ifx \normalvcenter \undefined \let \normalvcenter = \vcenter \fi

%D And even rawer:

\let\naturalhbox    \normalhbox
\let\naturalvbox    \normalvbox
\let\naturalvtop    \normalvtop
\let\naturalvcenter \normalvtop

\beginOMEGA dir

\def\naturalhbox{\normalhbox dir TLT}
\def\naturalvbox{\normalvbox dir TLT}
%def\naturalvtop{\normalvtop dir TLT}

\endOMEGA

%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:

% it's about time to drop tex in favour of etex / TEX VERSION TO BE TESTED

\beginTEX

  \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}

\endTEX

\beginETEX

  \def\setboxllx#1#2{\setevalue{b@@x\number#1}{\the\dimexpr(#2)}}
  \def\setboxlly#1#2{\setevalue{b@@y\number#1}{\the\dimexpr(#2)}}

  \def\gsetboxllx{\global\setboxllx}
  \def\gsetboxlly{\global\setboxlly}

\endETEX

\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}}

\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
