%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 ADE \& \CONTEXT\ Development Team}] %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 \newif\iftrialtypesetting \def\doiftextelse#1% {\bgroup \setbox\scratchbox\normalhbox {\trialtypesettingtrue \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 {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} %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{} {\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