%D \module
%D   [       file=core-rul,
%D        version=1998.10.16,
%D          title=\CONTEXT\ Core Macros,
%D       subtitle=Ruled Stuff Handling,
%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.

\writestatus{loading}{Context Core Macros / Ruled Content Handling}

%D This module is now etex dependent.

\unprotect

%D We have removed the rather old and out dated raster methods. They
%D have not been used for ages.

%D \macros
%D   {linewidth, setuplinewidth}
%D
%D This module deals with rules (lines) in several ways. First
%D we introduce two macros that can be used to set some common
%D characteristics.
%D
%D \showsetup{setuplinewidth}
%D
%D The linewidth is available in \type{\linewidth}. The
%D preset value of .4pt equals the default hard coded \TEX\
%D rule width.

\newdimen\linewidth

\def\dosetuplinewidth[#1]%
  {\assigndimension{#1}\linewidth{.2\points}{.4\points}{.6\points}}

\def\setuplinewidth
  {\dosingleargument\dosetuplinewidth}

%D \macros
%D   {ruledlinewidth, inheritruledlinewidth}
%D
%D Inside framed boxed we will use a private dimensions. As
%D an option one can let the linewidth inherit its value from
%D this one.

\newdimen\ruledlinewidth \newif\ifinheritruledlinewidth

% %D \TEX\ lacks support for color and even gray scales. The next
% %D macros can provide a sort of poor mans gray scales as well
% %D as give access to more suitable methods of rendering. Such a
% %D method looks like:
% %D
% %D \starttyping
% %D \def\methodegraybox#1#2#3#4#5#6%
% %D   { ... }
% %D \stoptyping
% %D
% %D The string \type{graybox} is a common element in the name,
% %D so we can have for instance \type {\postscriptgraybox} or
% %D \type {\texgraybox}. The first three arguments take a
% %D dimension, the fourth one takes a number between~0 and~1,
% %D and the last argument specifies a radius of the box when
% %D rounded corners are used, so:
% %D
% %D \startbuffer
% %D \dotgraybox{.5\hsize}{1cm}{0cm}{.85}{\v!no}{0pt}
% %D \stopbuffer
% %D
% %D \typebuffer
% %D
% %D becomes:
% %D
% %D %\startlinecorrection
% %D %  \vbox to 1cm{\getbuffer}
% %D %\stoplinecorrection
% %D
% %D \startlinecorrection
% %D   \unprotect
% %D   \vbox to 1cm{\dotgraybox{.5\hsize}{1cm}{0cm}{.85}{\v!no}{0pt}}
% %D   \protect
% %D \stoplinecorrection
% %D
% %D There are two predefined methodes, one uses periods and the
% %D other uses small rules. The second method is less
% %D efficient, but sometimes give better results. The dimensions
% %D of the resullting box are set to zero.
%
% \setvalue{\v!dot  graybox}{\processraster\symbol\rasterdot}
% \setvalue{\v!rule graybox}{\processraster\symbol\rasterbox}
%
% \def\rasterdot{\rasterfont.}
% \def\rasterbox{\hss\vrule\!!width.4pt\!!height.4pt\!!depth\zeropoint}
%
% %D Now of course we need:
%
% \ifx\rasterfont\undefined \def\rasterfont{\fivepoint} \fi
%
% %D We implement two pure \TEX\ based generators, that use
% %D \type{\leaders} to quickly gerenate the gray pattern. One
% %D should beware of \DIMENSION\ conflicts, so we use some
% %D registers above~8. These macros are memory hungry and byte
% %D spoiling.
%
% \def\processraster#1#2#3#4#5#6#7%
%   {\bgroup
%    \forgetall
%    \dontcomplain
%    \dimen10=\onepoint
%    \dimen10=\@@rsfactor\dimen10
%    \dimen10=#5\dimen10
%    \setbox2\hbox to #2
%      {\cleaders\hbox to 2\dimen10{#1\hss}\hss}%
%    \dimen12=#3%
%    \advance\dimen12 #4%
% %    \setbox0\vbox to \dimen12
%      {\cleaders\vbox to 2\dimen10{\box2\vss}\vss}%
%    \setbox0\hbox
%      {\hskip-.5\dimen10\lower0.5\dimen10\copy0
%       \hskip-\wd0\hskip\dimen10\lower1.5\dimen10\box0}%
%    \box0
%    \egroup}

%D \macros
%D   {setupscreens}
%D
%D The previous macro uses a predefined constant
%D \type{\@@rsfactor}. This factor can be set by:
%D
%D \showsetup{setupscreens}

\def\setupscreens
  {\dodoubleargument\getparameters[\??rs]}

% %D The most appropriate way to call for this feature is
% %D using \type{\graybox}, which is defined as:
%
% \def\graybox{\getvalue{\@@rsmethod graybox}}
%
% %D We just introduced two pure \TEX\ methods for generating
% %D rasters. However, it's far more efficient and comfortable in
% %D terms of speed, memory usage and file size, to use a driver
% %D supported method.
%
% \setvalue{\v!external graybox}{\setgraybox}
%
% %D For compatibility reasons we also define the original one:
%
% \setvalue{\v!postscript graybox}{\getvalue{\v!external graybox}}
%
% %D A quite valid way of letting drivers do the job, is giving
% %D a solid rule a gray texture.

%D We will communicate through module specific variables, current
%D framed parameters and some reserved dimension registers.

\newdimen \frameddimenwd
\newdimen \frameddimenht
\newdimen \frameddimendp

%D We don't have to stick to a \TEX\ drawn rule, but
%D also can use rounded or even fancier shapes, as we will
%D see later on.

\def\dofilledbox
  {\bgroup
   \doifelse{\framedparameter\c!backgroundcorner}\v!rectangular
     {\dofilledlinedbox}
     {\scratchdimen\framedparameter\c!backgroundradius\relax   % just in case of .x\bodyfontsize
      \ifzeropt\scratchdimen
        \dofilledlinedbox
      \else
        \dofilledroundbox
      \fi}%
   \egroup}


\def\dophantombox
  {\hphantom{\dofilledbox}}

\def\dofilledlinedbox
  {\vrule\!!width\frameddimenwd\!!height\frameddimenht\!!depth\frameddimendp\relax}%

\def\dofilledroundbox
  {\doroundedbox\v!off\v!on{\framedparameter\c!backgroundcorner}}

\def\doroundedbox#1#2#3%
  {\bgroup
   \doifelse{#3}\v!round{\let\ovalmod\!!zerocount}{\edef\ovalmod{\number#3}}%
   \edef\ovalwid{\the\frameddimenwd}%
   \edef\ovalhei{\the\frameddimenht}%
   \edef\ovaldep{\the\frameddimendp}%
   \scratchdimen\ruledlinewidth\edef\ovallin{\the\scratchdimen}%
   \scratchdimen\framedparameter\c!frameradius\edef\ovalrad{\the\scratchdimen}%
   \doifelse{#1}\v!on            % will be a macro that handles
     {\let\ovalstr\!!plusone}    % start, stop, ja, nee, aan, uit
     {\let\ovalstr\!!zerocount}% % and something \doif
   \doifelse{#2}\v!on
     {\let\ovalfil\!!plusone}
     {\let\ovalfil\!!zerocount}%
   \doovalbox\ovalwid\ovalhei\ovaldep\ovallin\ovalrad\ovalstr\ovalfil\ovalmod
   \egroup}

% a lot of weird corners
%
% \startTEXpage
%     \dontleavehmode\framed
%         [corner=0,frame=on,framecolor=green,
%          background=color,backgroundcolor=yellow]{\tttf TEST \twodigits\recurselevel}%
%     \vskip1em
%     \dontleavehmode\dostepwiserecurse {1} {4}{1}{\framed
%         [corner=\recurselevel,frame=on,framecolor=green,
%          background=color,backgroundcolor=yellow]{\tttf TEST \twodigits\recurselevel}%
%         \quad}
%     \vskip1em
%     \dontleavehmode\dostepwiserecurse {5} {8}{1}{\framed
%         [corner=\recurselevel,frame=on,framecolor=green,
%          background=color,backgroundcolor=yellow]{\tttf TEST \twodigits\recurselevel}%
%         \quad}
%     \vskip1em
%     \dontleavehmode\dostepwiserecurse {1} {4}{1}{\framed
%         [corner=\recurselevel,frame=on,framecolor=green]{\tttf TEST \twodigits\recurselevel}%
%         \quad}
%     \vskip1em
%     \dontleavehmode\dostepwiserecurse {5} {8}{1}{\framed
%         [corner=\recurselevel,frame=on,framecolor=green]{\tttf TEST \twodigits\recurselevel}%
%         \quad}
%     \vskip1em
%     \dontleavehmode\dostepwiserecurse {9}{12}{1}{\framed
%         [corner=\recurselevel,frame=on,framecolor=green]{\tttf TEST \twodigits\recurselevel}%
%         \quad}
%     \vskip1em
%     \dontleavehmode\dostepwiserecurse{13}{16}{1}{\framed
%         [corner=\recurselevel,frame=on,framecolor=green]{\tttf TEST \twodigits\recurselevel}%
%         \quad}
%     \vskip1em
%     \dontleavehmode\dostepwiserecurse{17}{20}{1}{\framed
%         [corner=\recurselevel,frame=on,framecolor=green]{\tttf TEST \twodigits\recurselevel}%
%         \quad}
%     \vskip1em
%     \dontleavehmode\dostepwiserecurse{21}{24}{1}{\framed
%         [corner=\recurselevel,frame=on,framecolor=green]{\tttf TEST \twodigits\recurselevel}%
%         \quad}
%     \vskip1em
%     \dontleavehmode\dostepwiserecurse{25}{28}{1}{\framed
%         [corner=\recurselevel,frame=on,framecolor=green]{\tttf TEST \twodigits\recurselevel}%
%         \quad}
% \stopTEXpage

%D The oval box is drawn using a special macro, depending on
%D the driver in use.

\def\dograybox % avoid black rules when no gray
  {\doifelsenothing{\framedparameter\c!backgroundscreen}
     {\dophantombox}
     {\raster[\framedparameter\c!backgroundscreen]{\dofilledbox}}}

%D It won't be a surprise that we not only provide gray boxes,
%D but also colored ones. Here it is:

\def\docolorbox
  {\hbox{\ifincolor
     \doifcolorelse{\framedparameter\c!backgroundcolor}
       {\localcolortrue\color[\framedparameter\c!backgroundcolor]{\dofilledbox}}
       {\dophantombox}%
   \else
     \dophantombox
   \fi}}

%D \macros
%D   {defineoverlay, doifoverlayelse, overlayoffset,
%D    overlaywidth, overlayheight, overlaydepth,
%D    overlaycolor, overlaylinecolor, overlaylinewidth}
%D
%D Before we define the macro that actually takes card of the
%D backgrounds, we introduce overlays. An overlay is something
%D that contrary to its name lays {\em under} the text. An
%D example of an overlay definition is:
%D
%D \startbuffer[tmp-1]
%D \defineoverlay
%D   [fancy]
%D   [{\externalfigure
%D       [mp-cont.502]
%D       [width=\overlaywidth,
%D        height=\overlayheight]}]
%D \stopbuffer
%D
%D \typebuffer[tmp-1]
%D
%D  That for instance can be uses in:
%D
%D  \startbuffer[tmp-2]
%D  \framed[backgroundachtergrond=fancy]{How Fancy!}
%D  \framed[backgroundachtergrond=fancy,frame=off]{Even More Fancy!}
%D  \stopbuffer
%D
%D  and looks like:
%D
%D  \startlinecorrection
%D  \vbox{\baselineskip24pt\getbuffer[tmp-1]\getbuffer[tmp-2]}
%D  \stoplinecorrection
%D
%D The formal definition is:
%D
%D \showsetup{defineoverlay}
%D
%D This macro's definition is a bit obscure, due the many
%D non||used arguments and the two step call that enable the
%D setting of the width, height and depth variables.
%D Multiple backgrounds are possible and are specified as:
%D
%D \starttyping
%D \framed[background={one,two,three}]{Three backgrounds!}
%D \stoptyping
%D
%D Most drawing packages only know width and height. Therefore
%D the dimensions have a slightly different meaning here:
%D
%D \startitemize[packed]
%D \item \type{\overlaywidth }: width of the overlay
%D \item \type{\overlayheight}: height plus depth of the overlay
%D \item \type{\overlaydepth }: depth of the overlay
%D \stopitemize
%D
%D The resulting box is lowered to the right depth.

\def\overlaywidth      {\the\hsize\space} % We preset the variables
\def\overlayheight     {\the\vsize\space} % to some reasonable default
\let\overlaydepth       \!!zeropoint      % values. The attributes
\let\overlayoffset      \!!zeropoint      % of the frame can be (are)
\let\overlaycolor       \empty            % set somewhere else.
\let\overlaylinewidth   \!!zeropoint      %
\let\overlaylinecolor   \empty            %

%D The next register is used to initialize overlays.

\newtoks\everyoverlay

%D An example of an initialization is the following (overlays
%D can contain text and be executed under an regime where
%D interlineskip is off).

\appendtoks \oninterlineskip \to \everyoverlay

\def\defineoverlay
  {\dodoubleargument\dodefineoverlay}

\def\dodefineoverlay[#1][#2]%
  {\def\docommand##1{\setvalue{\??ov##1}{\executedefinedoverlay{##1}{#2}}}%
   \processcommalist[#1]\docommand}

\prependtoks
  \hsize\overlaywidth
  \vsize\overlayheight
\to\everyoverlay

\long\def\executedefinedoverlay#1#2%
  {\bgroup
   \edef\overlaywidth {\the\frameddimenwd\space}%
   \edef\overlayheight{\the\dimexpr\frameddimenht+\frameddimendp\relax\space}%
   \edef\overlaydepth {\the\frameddimendp\space}%
   \edef\overlaycolor {\framedparameter\c!backgroundcolor}%
  %\edef\overlaycorner{\framedparameter\c!backgroundcorner}%
  %\edef\overlayradius{\framedparameter\c!backgroundradius}%
   \let\overlayoffset\backgroundoffset % we steal this one
   %\writestatus
   %  {overlay}
   %  {#1\space w=\overlaywidth/h+d=\overlayheight/d=\overlaydepth]}%
   \setbox\scratchbox\hbox{\lower\overlaydepth\hbox{\the\everyoverlay#2}}%
%    \dimen0=\dimexpr\wd\scratchbox-\overlaywidth \relax
%    \dimen2=\dimexpr\ht\scratchbox-\frameddimenht\relax % not overlayheight !
%    \setbox\scratchbox\hbox{\hskip-.5\dimen0\raise-.5\dimen2\box\scratchbox}%
   \setbox\scratchbox\hbox
     {\hskip-.5\dimexpr\wd\scratchbox-\overlaywidth \relax
      \raise-.5\dimexpr\ht\scratchbox-\frameddimenht\relax % not overlayheight !
      \box\scratchbox}%
   \wd\scratchbox\overlaywidth
   \ht\scratchbox\overlayheight
   \dp\scratchbox\overlaydepth
   \startlayoutcomponent{o:#1}{overlay #1}%
     \box\scratchbox
   \stoplayoutcomponent
   \egroup}

%D The empty case is:

\let\executeoverlay\gobblesevenarguments

%D For testing we provide:

\def\doifoverlayelse#1%
  {\doifdefinedelse{\??ov#1}}

%D We predefine two already familiar backgrounds:

\setvalue{\??ov\v!screen}{\dograybox }
\setvalue{\??ov\v!color }{\docolorbox}

% %D After all these preparations, the background macro does no
% %D bring to many surprises. One has to keep in mind that this
% %D macro starts up a call chain, depending on the background
% %D one needs:
% %D
% %D \startitemize[packed]
% %D \item a raster, color or user defined shape
% %D \item square or round corners
% %D \item a \TEX\ or driver based method
% %D \stopitemize
% %D
% %D The macro can be extended by adding commands to the token
% %D list register \type {\everybackgroundbox}. For this
% %D purpose, the name of the current background is available in
% %D \type {\currentbackgound}.

\newbox\extraframebox

\newtoks\everybackgroundbox

\let\currentbackground\empty

\def\dodobackgroundbox#1% also less passing, we can get rid of the old method
  {\bgroup
   \def\currentbackground{#1}%
   \the\everybackgroundbox
   \setbox\extraframebox\hbox
     {\vbox{\moveleft\backgroundoffset\hbox{\executeifdefined{\??ov\currentbackground}\donothing}}}%
   \wd\extraframebox\zeropoint % \backgroundwidth
   \ht\extraframebox\backgroundheight
   \dp\extraframebox\backgrounddepth
   \box\extraframebox % \hskip-\backgroundwidth
   \egroup}

\let\backgroundoffset\!!zeropoint
\let\backgrounddepth \!!zeropoint
\def\backgroundwidth {\the\hsize}
\def\backgroundheight{\the\vsize}

%  todo: also \def\theforegroundbox{#1}

\def\dobackgroundbox#1%
  {\setbox\framebox\vbox
     {\forgetall
      \boxmaxdepth\maxdimen
      \scratchdimen \framedparameter{#1}\relax
      \frameddimenwd\dimexpr\wd\framebox+2\scratchdimen\relax
      \frameddimenht\dimexpr\ht\framebox+ \scratchdimen\relax
      \frameddimendp\dimexpr\dp\framebox+ \scratchdimen+\framedparameter\c!backgrounddepth\relax
      \edef\backgroundoffset{\the\scratchdimen}%
      \edef\backgroundwidth {\the\wd\framebox}%
      \edef\backgroundheight{\the\ht\framebox}%
      \edef\backgrounddepth {\the\dp\framebox}%
     %\edef\foregroundbox{\box#1}%
      \def\foregroundbox% fuzzy but needed hack, this \vss, otherwise
        {\vbox to \backgroundheight{\vss\box\framebox\vss}}% vertical shift
      \edef\component{\framedparameter\c!component}%
      \hbox to \backgroundwidth % in case 'foreground' is used as overlay
        {\ifx\component\empty
           \expanded{\rawprocesscommalist[\framedparameter\c!background]}\dodobackgroundbox
         \else
           \startlayoutcomponent{b:\component}{background \component}%
           \expanded{\rawprocesscommalist[\framedparameter\c!background]}\dodobackgroundbox
           \stoplayoutcomponent
         \fi
         \box\framebox\hss}}}

%D One can explictly insert the foreground box. For that
%D purpose we introduce the overlay \type {foreground}.

\defineoverlay[\v!foreground][\foregroundbox]

%D We can specify overlays as a comma separated list of
%D overlays, a sometimes handy feature.

%D Besides backgrounds (overlays) we also need some macros to
%D draw outlines (ruled borders). Again we have to deal with
%D square and round corners. The first category can be handled
%D by \TEX\ itself, the latter one depends on the driver. This
%D macro also support a negative offset.

\def\dooutlinebox
  {\setbox\framebox\vbox % rules on top of box
     {\scratchdimen \framedparameter\c!frameoffset\relax
      \frameddimenwd\dimexpr\wd\framebox+2\scratchdimen\relax
      \frameddimenht\dimexpr\ht\framebox+ \scratchdimen\relax
      \frameddimendp\dimexpr\dp\framebox+ \scratchdimen+\framedparameter\c!framedepth\relax
      \ifdim\frameddimendp<\zeropoint
        \advance\frameddimenht \frameddimendp
        \scratchdimen-\frameddimendp
        \frameddimendp\zeropoint
      \else
        \scratchdimen\zeropoint
      \fi
      \setbox\extraframebox\hbox
        {\dostrokedbox}%
      \setbox\extraframebox\hbox
        {\raise\scratchdimen\vbox
           {\moveleft\framedparameter\c!frameoffset\box\extraframebox}}%
      \wd\extraframebox\wd\framebox
      \ht\extraframebox\ht\framebox
      \dp\extraframebox\dp\framebox
      \hbox
        {\box\framebox\hskip-\wd\extraframebox
         \doifsomething{\framedparameter\c!framecolor}% no else needed,
           {\color[\framedparameter\c!framecolor]}%   %
           {\box\extraframebox}}}}                    % but {} here

\def\dostrokedbox
  {\doifelse{\framedparameter\c!framecorner}\v!rectangular
     {\dostrokedlinedbox}
     {\ifzeropt\dimexpr\framedparameter\c!frameradius\relax   % just in case of .x\bodyfontsize
        \dostrokedlinedbox
      \else
        \dostrokedroundbox
      \fi}}

\def\dostrokedlinedbox
  {\setbox\scratchbox\null
   \wd\scratchbox\frameddimenwd
   \ht\scratchbox\frameddimenht
   \dp\scratchbox\frameddimendp
   \setbox\scratchbox\vbox \bgroup
     \csname   t\@@frame@@\framedparameter\c!frame\framedparameter\c!topframe   \endcsname
     \hbox \bgroup
       \csname l\@@frame@@\framedparameter\c!frame\framedparameter\c!leftframe  \endcsname
       \box\scratchbox
       \csname r\@@frame@@\framedparameter\c!frame\framedparameter\c!rightframe \endcsname
     \egroup
     \csname   b\@@frame@@\framedparameter\c!frame\framedparameter\c!bottomframe\endcsname
   \egroup
   \wd\scratchbox\frameddimenwd
   \ht\scratchbox\frameddimenht
   \dp\scratchbox\frameddimendp
   \box\scratchbox}

\def\@@frame@@{@@frame@@}

\setvalue{t\@@frame@@\v!on \v!on}{\hrule\!!height\ruledlinewidth\kern-\ruledlinewidth}
\setvalue{t\@@frame@@\v!off\v!on}{\hrule\!!height\ruledlinewidth\kern-\ruledlinewidth}
\setvalue{t\@@frame@@\v!on      }{\hrule\!!height\ruledlinewidth\kern-\ruledlinewidth}
\setvalue{b\@@frame@@\v!on \v!on}{\kern-\ruledlinewidth\hrule\!!height\ruledlinewidth}
\setvalue{b\@@frame@@\v!off\v!on}{\kern-\ruledlinewidth\hrule\!!height\ruledlinewidth}
\setvalue{b\@@frame@@\v!on      }{\kern-\ruledlinewidth\hrule\!!height\ruledlinewidth}
\setvalue{l\@@frame@@\v!on \v!on}{\vrule\!!width\ruledlinewidth\kern-\ruledlinewidth}
\setvalue{l\@@frame@@\v!off\v!on}{\vrule\!!width\ruledlinewidth\kern-\ruledlinewidth}
\setvalue{l\@@frame@@\v!on      }{\vrule\!!width\ruledlinewidth\kern-\ruledlinewidth}
\setvalue{r\@@frame@@\v!on \v!on}{\kern-\ruledlinewidth\vrule\!!width\ruledlinewidth}
\setvalue{r\@@frame@@\v!off\v!on}{\kern-\ruledlinewidth\vrule\!!width\ruledlinewidth}
\setvalue{r\@@frame@@\v!on      }{\kern-\ruledlinewidth\vrule\!!width\ruledlinewidth}

%D I condidered using the low level support command
%D \type{\ruledhbox}, but this would slow down processing by a
%D factor~3.

%D Before we come to using these macros we yet have to define
%D the rounded corner alternative, which after all is not that
%D hard because it uses the previously defined macro \type
%D {\doroundedbox}.

\def\dostrokedroundbox
  {\doif{\framedparameter\c!frame}\v!on{\doroundedbox\v!on\v!off{\framedparameter\c!framecorner}}}

%D The next few macros are probably the most misused ones in
%D \CONTEXT. They deal with putting rules around boxes, provide
%D backgrounds, offer alignment features, and some more. We
%D start with defining some booleans. These give an impression
%D of what we are going to take into account.

% todo: chardefs

\newif\ifboxhasoffset
\newif\ifboxhaswidth
\newif\ifboxhasheight
\newif\ifboxhasformat
\newif\ifboxhasstrut
\newif\ifboxisoverlaid
\newif\ifboxhasframe
\newif\ifdelayedstrut

%D We also need a few \DIMENSIONS:

\newdimen\@@localoffset
\newdimen\@@globalwidth

%D The content of the box will be (temporary) saved in a box:

\newbox\framebox

%D We also need a box for outlines and backgrounds:

\newbox\extraframebox

%D \macros
%D   {framed, setupframed}
%D
%D Ruled boxes are typeset using \type{\framed}. This command
%D is quite versatile and, although some users will probably
%D seldom use it, one cannot overlook its features.
%D
%D \showsetup{setupframed}
%D \showsetup{framed}
%D
%D This general macro is a special version of an even more
%D general case, that can easily be linked into other macros
%D that need some kind of framing. The local version is called
%D with an extra parameter: the variable identifier. The reason
%D for passing this identifier between brackets lays in the
%D mere fact that this way we can use the optional argument
%D grabbers.

\def\defaultframeoffset{.25ex}

\unexpanded\def\framed
  {\bgroup
   \presetlocalframed[\??ol]%
   \dodoubleempty\startlocalframed[\??ol]}

\def\presetlocalframed[#1]%
  {\copylocalframed[#1][\??oi]}

\def\copylocalframed[#1]#2[#3]%
  {\copyparameters[#1][#3]%
     [\c!width,\c!height,\c!autowidth,\c!offset,\c!empty,\c!rulethickness,%
      \c!radius,\c!corner,\c!depth,\c!frame,\c!framecolor,%
      \c!foregroundstyle,\c!foregroundcolor,\c!lines,\c!orientation,%
      \c!topframe,\c!bottomframe,\c!leftframe,\c!rightframe,%
      \c!rulethickness,\c!frameoffset,\c!background,\c!component,%
      \c!backgroundoffset,\c!backgroundscreen,\c!backgroundcolor,%
      \c!align,\c!bottom,\c!top,\c!strut,\c!autostrut,\c!location,\c!setups,\c!extras]%
   \copyparameters[#1\c!background][#1]%
     [\c!radius,\c!corner,\c!depth]%
   \copyparameters[#1\c!frame][#1]%
     [\c!radius,\c!corner,\c!depth]}

% \def\setupframed
%   {\dodoubleargument\getparameters[\??oi]}

\def\setupframed
  {\dodoubleempty\dosetupframed}

\def\dosetupframed
  {\ifsecondargument
     \@EA\dodoublesetupframed
   \else
     \@EA\dosinglesetupframed
   \fi}

\def\dosinglesetupframed[#1][#2]%
  {\getparameters[\??oi][#1]}

\def\dodoublesetupframed[#1][#2]%
  {\bgroup
   \let\dodoubleempty\empty
   \def\doframed[##1]{\gdef\globalredefinedframed{\dodoubleempty\doframed[##1,#2]}}%
   \getvalue{#1}%
   \egroup
   \letvalue{#1}\globalredefinedframed}

%D \startbuffer
%D \setupframed [framecolor=yellow]          \framed{A}
%D \defineframed[myframed] [framecolor=blue] \myframed{B}
%D \setupframed [myframed] [framecolor=red]  \myframed{C}
%D \stopbuffer
%D
%D \typebuffer \getbuffer
%D
%D \startbuffer
%D \presetlocalframed[myframed]
%D \setuplocalframed[myframed][width=4cm,height=2cm]
%D \localframed[myframed][framecolor=green]{oeps}
%D \stopbuffer
%D
%D \typebuffer \getbuffer

%D \macros
%D   {ifinframed}
%D
%D The normal case first presets all parameters and next starts
%D looking for the user supplied ones. The first step is
%D omitted in the local case, because these are preset at
%D declaration time and keep their values unless explictly
%D changed. By presetting the variables everytime the normal
%D command is called, we can use this command nested, without
%D the unwanted side effect of inheritance. The boolean is
%D used to speed up the color stack.

\newif\ifinframed

\def\localframed
  {\bgroup
   \dodoubleempty\startlocalframed}

%D The next one is faster on multiple backgrounds per page. No
%D dimensions can be set, only frames and backgrounds.

\def\fastlocalframed[#1]#2[#3]#4% 3-4
  {\bgroup
   \inframedtrue
   \edef\@@framed{#1}%
   % more bytes
   % \scratchdimen\framedparameter\c!frameoffset
   % \setevalue{\@@framed\c!frameoffset}{\the\scratchdimen}%
   % \doifnotvalue{\@@framed\c!backgroundoffset}\v!frame
   %   {\scratchdimen\framedparameter\c!backgroundoffset
   %    \setevalue{\@@framed\c!backgroundoffset}{\the\scratchdimen}}%
   % less bytes
   \@EA\freezedimenmacro\csname\@@framed\c!frameoffset\endcsname
   \doifnotvalue{\@@framed\c!backgroundoffset}\v!frame
     {\@EA\freezedimenmacro\csname\@@framed\c!backgroundoffset\endcsname}%
   % so far
   \setbox\framebox\hbox{#4}%
   \getparameters[\@@framed][#3]% no \expanded !
   % no, better in calling macro
   %
   %    \edef\doframedsetups{\framedparameter\c!setups}%
   %    \ifx\doframedsetups\empty\else
   %      \edef\doframedsetups{\noexpand\setups[\doframedsetups]}%
   %    \fi
   \removeframedboxdepth
   \docolorframebox
   \edef\overlaylinecolor{\framedparameter\c!framecolor}%
   \edef\overlaylinewidth{\the\ruledlinewidth}%
   \edef\@@localframing {\framedparameter\c!frame}%
   \ifx\@@localframing\v!overlay \else \ifx\@@localframing\v!none \else
     \doifvaluesomething{\@@framed\c!rulethickness}
       {\ruledlinewidth\framedparameter\c!rulethickness\relax
        \ifinheritruledlinewidth\linewidth\ruledlinewidth\fi}%
     \dooutlinebox % real or invisible frame
   \fi \fi
   \doifvaluesomething{\@@framed\c!background}\dobackedbox
   \restoreframedboxdepth
   \box\framebox
   \egroup}

%D Before we go into details, we present (and implement) the
%D main framing routine. I saw no real reason for splitting the
%D next two macros into smaller pieces. The content will be
%D collected in a horizontal or vertical box with fixed or free
%D dimensions and specific settings concerning aligment and
%D offsets.
%D
%D In the first few lines, we pre||expand the frame and
%D background offsets. We do so, because the can be defined in
%D terms of the main offset. However, see for instance page
%D backgrounds, when \type {#2} sets the offset to \type
%D {overlay}, both offsets become invalid.
%D
%D Because it is used so often the he next macro is (and
%D looks) rather optimized.

\let\postprocessframebox\relax

\let\@@framed\s!unknown

\def\framedparameter#1%
  {\csname\@@framed#1\endcsname}

\newdimen\!!framedwidth
\newdimen\!!framedheight

\def\startlocalframed[#1][#2]%
  {\bgroup
   \inframedtrue
   \edef\@@framed{#1}%
   % this piece of pre expansion is needed (sometimes used in frameoffset)
   % \doifvaluesomething{\@@framed\c!rulethickness}         % obsolete
   %   {\ruledlinewidth\getvalue{\@@framed\c!rulethickness}}% obsolete
   % this piece of pre expansion is needed (sometimes used circular)
   \setevalue{\@@framed\c!frameoffset}{\the\dimexpr\framedparameter\c!frameoffset\relax}%
   \doifnotvalue{\@@framed\c!backgroundoffset}\v!frame
     {\setevalue{\@@framed\c!backgroundoffset}{\the\dimexpr\framedparameter\c!backgroundoffset\relax}}%
   % to prevent deadlock in case of self refering
   \ifsecondargument % faster
     \getparameters[\@@framed][#2]% here !
   \fi
   % new, experimental dirty hook
   \framedparameter\c!extras
   % we need to register the (outer) color
   \startregistercolor[\framedparameter\c!foregroundcolor]%
   % to get the right spacing
   \doifvaluesomething{\@@framed\c!foregroundstyle}
     {\@EA\doconvertfont\csname\@@framed\c!foregroundstyle\endcsname\empty}%
   % beware, both the frame and background offset can be overruled
   %
   \edef\doframedsetups{\framedparameter\c!setups}%
   \ifx\doframedsetups\empty\else
     \edef\doframedsetups{\noexpand\setups[\doframedsetups]}%
   \fi
   % the next macros are visible
   \edef\localoffset{\framedparameter\c!offset}%
   \edef\localwidth {\framedparameter\c!width}%
   \edef\localheight{\framedparameter\c!height}%
   \edef\localformat{\framedparameter\c!align}%
   \edef\localstrut {\framedparameter\c!strut}%
   % these are not
   \edef\@@localautostrut  {\framedparameter\c!autostrut}%
   \edef\@@localframing    {\framedparameter\c!frame}%
   \edef\@@locallocation   {\framedparameter\c!location}%
   \edef\@@localorientation{\framedparameter\c!orientation}%
   %
   \edef\@@localautowidth  {\framedparameter\c!autowidth}%
   %
   \ifx\@@localframing\v!overlay   % no frame, no offset, no framewidth
     \boxhasframefalse
     \let\localoffset\v!overlay
   \else\ifx\@@localframing\v!none % no frame, no framewidth
     \boxhasframefalse
   \else
     \boxhasframetrue
   \fi\fi
   \ifboxhasframe
     \doifvaluesomething{\@@framed\c!rulethickness}
       {\ruledlinewidth\framedparameter\c!rulethickness\relax
        \ifinheritruledlinewidth\linewidth\ruledlinewidth\fi}%
   \else
     \ruledlinewidth\zeropoint
   \fi
   \ifx\localformat\empty
     \boxhasformatfalse
   \else
     \boxhasformattrue
     \dosetraggedcommand\localformat
     \edef\dobeforeframedbox{\raggedtopcommand\framedparameter\c!top}%
     \edef\doafterframedbox {\framedparameter\c!bottom\raggedbottomcommand}%
   \fi
   \ifx\localoffset\v!none
     \boxhasoffsetfalse
     \boxhasstrutfalse
     \boxisoverlaidfalse
     \@@localoffset\ruledlinewidth
   \else\ifx\localoffset\v!overlay
   % \ifx\@@localframing\v!no \boxhasframefalse \fi % test first
     \boxhasoffsetfalse
     \boxhasstrutfalse
     \boxisoverlaidtrue
     \@@localoffset\zeropoint
   \else
     \boxhasoffsettrue
     \boxhasstruttrue
     \boxisoverlaidfalse
     \ifx\localoffset\v!default % new per 2-6-2000
       \let\localoffset\defaultframeoffset
       \letvalue{\@@framed\c!offset}\defaultframeoffset
     \else
       \let\defaultframeoffset\localoffset
     \fi
     \@@localoffset\dimexpr\localoffset+\ruledlinewidth\relax
   \fi\fi
   \!!framedheight\zeropoint
   \!!framedwidth \zeropoint
   \ifx\localwidth\v!fit
     \ifboxhasformat
       \boxhaswidthtrue
       \!!framedwidth\hsize
     \else
       \boxhaswidthfalse
     \fi
   \else\ifx\localwidth\v!fixed % equals \v!fit but no shapebox
     \ifboxhasformat
       \boxhaswidthtrue
       \!!framedwidth\hsize
     \else
       \boxhaswidthfalse
     \fi
   \else\ifx\localwidth\v!broad
     \boxhaswidthtrue
     \!!framedwidth\hsize
   \else\ifx\localwidth\v!local
     \boxhaswidthtrue
     \setlocalhsize
     \!!framedwidth\localhsize
   \else
     \boxhaswidthtrue
     \!!framedwidth\localwidth
   \fi\fi\fi\fi
   \ifx\localheight\v!fit
     \boxhasheightfalse % no longer: \boxhasstrutfalse
   \else\ifx\localheight\v!broad
     \boxhasheightfalse
   \else
     \boxhasheighttrue
     \!!framedheight\localheight
   \fi\fi
   \ifboxhasheight
     % obey user set height, also downward compatible
   \else
     \doifvaluesomething{\@@framed\c!lines}
       {\ifcase\framedparameter\c!lines\else
          \!!framedheight\framedparameter\c!lines\lineheight
          \edef\localheight{\the\!!framedheight}%
          \boxhasheighttrue
        \fi}%
   \fi
   % this is now an option: width=local
   %
   % \ifdim\!!framedwidth=\hsize
   %   \parindent\zeropoint
   %   \setlocalhsize
   %   \!!framedwidth\localhsize
   % \fi
   % i.e. disable (colsetbackgroundproblemintechniek)
   \advance\!!framedwidth  -2\@@localoffset
   \advance\!!framedheight -2\@@localoffset
   \ifx\localstrut\v!no
     \boxhasstrutfalse
   \else\ifx\localstrut\v!global
     \setstrut
   \else\ifx\localstrut\v!local
     \setfontstrut
   \else
   \fi\fi\fi
   \ifboxhasstrut
     \setstrut
     \let\localbegstrut\begstrut
     \let\localendstrut\endstrut
     \let\localstrut   \strut
   \else
     \let\localbegstrut\pseudobegstrut % was: \relax
     \let\localendstrut\pseudoendstrut % was: \relax
     \let\localstrut   \pseudostrut    % was: \relax
    %\ifboxhasheight\ifdim\!!framedheight<\strutht % saveguard
    %  \let\localbegstrut\relax               % but not that
    %  \let\localstrut   \relax               % save after all
    %\fi\fi
   \fi
   \ifx\@@localautostrut\v!yes
     \let\delayedbegstrut\relax
     \let\delayedendstrut\relax
     \let\delayedstrut   \relax
   \else
     \let\delayedbegstrut\localbegstrut
     \let\delayedendstrut\localendstrut
     \let\delayedstrut   \localstrut
     \let\localbegstrut  \relax
     \let\localendstrut  \relax
     \let\localstrut     \relax
   \fi
   \ifboxhasheight
     \let\\\vboxednewline
     \ifboxhaswidth
       \let\hairline\vboxedhairline
       \ifboxhasformat
         \let\next\doformatboxSomeFormat
       \else
         \let\next\doformatboxNoFormat
       \fi
     \else
       \let\hairline\hboxedhairline
       \ifboxhasformat
         \let\next\doformatboxHeight
       \else
         \let\next\doformatboxVSize
       \fi
     \fi
   \else
     \ifboxhaswidth
       \ifboxhasformat
         \let\hairline\vboxedhairline
         \let\\\vboxednewline
         \let\next\doformatboxWidth
       \else
         \let\hairline\hboxedhairline
         \let\\\hboxednewline
         \let\next\doformatboxHSize
       \fi
     \else
       \let\hairline\hboxedhairline
       \let\\\hboxednewline
       \let\next\doformatboxNoSize
     \fi
   \fi
   \edef\framedwidth % a new feature, visible for user
     {\ifdim\!!framedwidth >\zeropoint\the\!!framedwidth \else\zeropoint\fi}%
   \edef\framedheight% a new feature, visible for user
     {\ifdim\!!framedheight>\zeropoint\the\!!framedheight\else\zeropoint\fi}%
   % first alternative
   %\def\dowithframedbox%
   %  {\let\postprocessframebox\relax %new
   %   \aftergroup\stoplocalframed}%
   % \afterassignment\dowithframedbox
   % \setbox\framebox=\next}
   % second alternative
   %\dowithnextbox
   %  {\setbox\framebox\flushnextbox
   %   \let\postprocessframebox\relax %new
   %   \stoplocalframed}
   %  \next}
   % third alternative
   \@@startframedorientation
   \afterassignment\dodowithframebox
   \setbox\framebox\next}

\def\dowithframebox
  {% moved : \let\postprocessframebox\relax
   \stoplocalframed}

\def\dodowithframebox
  {\aftergroup\dowithframebox}

\let\doafterframedbox \relax
\let\dobeforeframedbox\relax

%D Carefull analysis of this macro will learn us that not all
%D branches in the last conditionals can be encountered, that
%D is, some assignments to \type{\next} will never occur.
%D Nevertheless we implement the whole scheme, if not for
%D future extensions.

%D \macros
%D   {ifreshapeframebox}
%D
%D The last few lines tell what to do after the content of the
%D box is collected and passed to the next macro. In the case
%D of a fixed width and centered alignment, the content is
%D evaluated and used to determine the most natural width. The
%D rest of the code deals with backgrounds and frames.

\newif\ifreshapeframebox \reshapeframeboxtrue

%D Beware: setting \type {top} and \type {bottom} to nothing, may
%D result in a frame that is larger that the given height! try:
%D
%D \starttyping
%D \framed
%D   [height=3cm,top=,bottom=,offset=overlay]
%D   {\strut test \shapefill \strut test}
%D \stoptyping
%D
%D This is intended behaviour and not a bug! One can always set
%D
%D \starttyping
%D ...,bottom=\kern0pt,...
%D \stoptyping

\def\stoplocalframed
  {\dontshowcomposition
   \@@stopframedorientation % hm, wrong place ! should rotatethe result (after reshape)
   \handleframedlocator\c!before\@@locallocation
   \ifboxhasformat
     \ifx\@@localautowidth\v!force
       \ifreshapeframebox\doreshapeframedbox\fi
       \boxhaswidthfalse
     \else
       \ifx\localwidth\v!fit
         \ifx\@@localautowidth\v!yes
           \ifreshapeframebox\doreshapeframedbox\fi
         \fi
         \boxhaswidthfalse
       \else\ifx\localwidth\v!fixed
         \boxhaswidthfalse
       \else
         \resetshapeframebox
       \fi\fi
     \fi
   \else
     \resetshapeframebox
   \fi
   \ifboxhaswidth
     \wd\framebox\!!framedwidth
   \fi
   \ifboxhasheight
     \ht\framebox\!!framedheight
   \fi
   \doifvalue{\@@framed\c!empty}\v!yes
     {\setbox\scratchbox\null
      \wd\scratchbox\wd\framebox
      \ht\scratchbox\ht\framebox
      \dp\scratchbox\dp\framebox
      \setbox\framebox\box\scratchbox}%
   \stopregistercolor
   \docolorframebox
   \ifboxhasoffset
     \dooffsetframebox
   \fi
   \ifboxisoverlaid \else
     \dolocateframebox
   \fi
   \ifx\postprocessframebox\relax \else
     \let\next\postprocessframebox
     \let\postprocessframebox\relax % prevent nesting
     \next\framebox
   \fi
   \edef\overlaylinecolor{\framedparameter\c!framecolor}%
   \edef\overlaylinewidth{\the\ruledlinewidth}% \@@...
   \ifboxhasframe % real or invisible frame
     \dooutlinebox
   \fi
   \doifvaluesomething{\@@framed\c!background}\dobackedbox
   \handleframedlocator\c!after\@@locallocation
   \box\framebox
   \egroup
   \egroup}

\def\installframedlocator#1#2#3%
  {\setvalue{\??ol:\c!location:\c!before:#1}{#2}%
   \setvalue{\??ol:\c!location:\c!after :#1}{#3}}

\def\handleframedlocator#1#2%
  {\getvalue{\??ol:\c!location:#1:#2}}

\def\doprelocframedbox#1%
  {\scratchdimen\dimexpr#1+\ruledlinewidth\relax
   \ifboxhasoffset
     \advance\scratchdimen \framedparameter\c!offset
   \fi
   \scratchskip\dimexpr\ht\framebox-\scratchdimen\relax}

% \ruledhbox
%   {A
%    \framed[width=2cm,align=middle,location=hanging]{location\\equals\\hanging}
%    \framed[width=2cm,align=middle,location=depth]  {location\\equals\\depth}
%    \framed[width=2cm,align=middle,location=height] {location\\equals\\height}
%    B}
% \vskip2cm
% \ruledhbox
%   {A
%    \framed[width=2cm,align=middle,location=low]    {location\\equals\\low}
%    \framed[width=2cm,align=middle,location=line]   {location\\equals\\line}
%    \framed[width=2cm,align=middle,location=high]   {location\\equals\\high}
%    B}
% \vskip2cm
% \ruledhbox
%  {A
%   \framed[width=2cm,align=middle,location=top]    {location\\equals\\top}
%   \framed[width=2cm,align=middle,location=bottom] {location\\equals\\bottom}
%   \framed[width=2cm,align=middle,location=lohi]   {location\\equals\\lohi}
%   \framed[width=2cm,align=middle,location=middle] {location\\equals\\middle}
%   B}

\installframedlocator \v!hanging % best with strut=no
  {}
  {\dp\framebox\ht\framebox
   \ht\framebox\zeropoint}

\installframedlocator \v!depth
  {}
  {\ht\framebox\dimexpr\ht\framebox-\strutdp\relax
   \dp\framebox\strutdp
   \box\framebox}

\installframedlocator \v!height
  {}
  {\dp\framebox\dimexpr\ht\framebox-\strutht\relax
   \ht\framebox\strutht
   \box\framebox}

\installframedlocator \v!high
  {}
  {\doprelocframedbox\strutht
   \setbox\framebox\hbox{\lower\scratchskip\box\framebox}%
   \ht\framebox\strutht
   \dp\framebox\strutdp
   \hbox{\box\framebox}}

\installframedlocator \v!line
  {}
  {\setbox\framebox\hbox{\lower.5\ht\framebox\box\framebox}%
   \ht\framebox.5\lineheight
   \dp\framebox.5\lineheight
   \hbox{\box\framebox}}

\installframedlocator \v!low
  {}
  {\doprelocframedbox\strutdp
   \setbox\framebox\hbox{\lower\scratchdimen\box\framebox}%
   \ht\framebox\strutht
   \dp\framebox\strutdp
   \box\framebox}

\installframedlocator \v!top
  {}
  {\doprelocframedbox\strutht
   \setbox\framebox\hbox{\lower\scratchskip\box\framebox}%
   \ht\framebox\scratchdimen
   \dp\framebox\scratchskip
   \hbox{\box\framebox}}

\installframedlocator \v!middle
  {}
  {\scratchdimen.5\ht\framebox
   \setbox\framebox\hbox{\lower\scratchdimen\box\framebox}%
   \ht\framebox\scratchdimen
   \dp\framebox\scratchdimen
   \hbox{\box\framebox}}

\installframedlocator \v!lohi
  {\handleframedlocator\c!before\v!middle}
  {\handleframedlocator\c!after \v!middle}

\installframedlocator \v!bottom
  {}
  {\doprelocframedbox\strutdp
   \setbox\framebox\hbox{\lower\scratchdimen\box\framebox}%
   \ht\framebox\scratchskip
   \dp\framebox\scratchdimen
   \hbox{\box\framebox}}

\installframedlocator \v!keep % retains height/depth
  {\removeframedboxdepth}
  {\restoreframedboxdepth}

% also used in fastlocalframed

\newdimen\originalframedwd
\newdimen\originalframedht
\newdimen\originalframeddp

\def\removeframedboxdepth
  {\originalframedwd\wd\framebox
   \originalframedht\ht\framebox
   \originalframeddp\dp\framebox
   \ifzeropt\originalframeddp\else\setbox\framebox\hbox{\raise\originalframeddp\box\framebox}\fi
   \wd\framebox\originalframedwd
   \ht\framebox\dimexpr\originalframedht+\originalframeddp\relax
   \dp\framebox\zeropoint}

\def\restoreframedboxdepth
  {\ifzeropt\originalframeddp\else\setbox\framebox\hbox{\lower\originalframeddp\box\framebox}\fi
   \wd\framebox\originalframedwd
   \ht\framebox\originalframedht
   \dp\framebox\originalframeddp}

% \let\@@startframedorientation\relax
% \let\@@stopframedorientation \relax

% \framed[width=12cm,height=3cm,orientation=0]{\input ward\relax}
% \framed[width=12cm,height=3cm,orientation=90]{\input ward\relax}
% \framed[width=12cm,height=3cm,orientation=180]{\input ward\relax}
% \framed[width=12cm,height=3cm,orientation=270]{\input ward\relax}
% \framed[width=12cm,height=3cm,orientation=-90]{\input ward\relax}
% \framed[width=12cm,height=3cm,orientation=-180]{\input ward\relax}
% \framed[width=12cm,height=3cm,orientation=-270]{\input ward\relax}

\def\@@startframedorientation
  {\let\@@stopframedorientation \relax
   \ifx\@@localorientation\empty\else
     \ifcase\@@localorientation\else
       \scratchcounter\@@localorientation
       \divide\scratchcounter\plustwo
       \ifodd\scratchcounter
         \swapmacros\framedwidth   \framedheight
         \swapmacros\localwidth    \localheight
         \swapdimens\!!framedheight\!!framedwidth
         \def\@@stopframedorientation{\@@dostopframedorientation\plusone}%
       \else
         \def\@@stopframedorientation{\@@dostopframedorientation\zerocount}%
       \fi
     \fi
   \fi}

\def\@@dostopframedorientation#1%
  {\ifcase#1\else
     \swapmacros\framedwidth   \framedheight
     \swapmacros\localwidth    \localheight
     \swapdimens\!!framedheight\!!framedwidth
   \fi
   \setbox\framebox\hbox{\dorotatebox\@@localorientation\hbox{\box\framebox}}}

%D The last conditional takes care of the special situation of
%D in||line \inframed[height=3cm]{framed} boxes. Such boxes have
%D to be \inframed{aligned} with the running text.

\def\doinframed[#1]% we could omit #1] but readibility ...
  {\framed[\c!location=\v!low,#1]}

\unexpanded\def\inframed
  {\dosingleempty\doinframed}

%D When we set \type{empty} to \type{yes}, we get
%D ourselves a frame and/or background, but no content, so
%D actually we have a sort of phantom framed box.

%D Because color marks and specials can interfere with
%D spacing, we provide a way to specify a foregroundcolor.

\def\docolorframebox
  {\doifvaluesomething{\@@framed\c!foregroundcolor}
     {\doifcolorelse{\framedparameter\c!foregroundcolor}
        {\setbox\framebox\hbox
           {\localcolortrue
            \color[\framedparameter\c!foregroundcolor]{\box\framebox}}}
        {}}}

%D \macros
%D   {mframed, minframed}
%D
%D When Tobias asked how to frame mathematical elements in
%D formulas, Taco's posted the next macro:
%D
%D \starttyping
%D \def\mframed#1%
%D   {\relax
%D    \ifmmode
%D      \vcenter{\hbox{\framed{$\ifinner\else\displaystyle\fi#1$}}}%
%D    \else
%D      \framed{$#1$}%
%D    \fi}
%D \stoptyping
%D
%D Because \type {\ifinner} does not (always) reports what
%D one would expect, we move the test to the outer level. We
%D also want to pass arguments,
%D
%D \starttyping
%D \def\mframed%
%D   {\dosingleempty\domframed}
%D
%D \def\domframed[#1]#2% % tzt \dowithnextmathbox ?
%D   {\relax
%D    \ifmmode
%D      \ifinner
%D        \inframed[#1]{$#2$}%
%D      \else
%D        \vcenter{\hbox{\framed[#1]{$\displaystyle#2$}}}%
%D      \fi
%D    \else
%D      \inframed[#1]{$#2$}%
%D    \fi}
%D \stoptyping
%D
%D Still better is the next alternative, if only because it
%D takes care of setting the super- and subscripts styles

\ifx\restoremathstyle\undefined \let\restoremathstyle\relax \fi

\def\domframed[#1][#2]#3%
  {\begingroup
   \ifmmode
     \ifinner
       \let\mframedstyle\restoremathstyle
     \else
       \let\mframedstyle\displaystyle
     \fi
   \else
     \let\mframedstyle\restoremathstyle
   \fi
   #1\ifdone
     \def\normalstrut{$\mframedstyle\vphantom($}%
     \framed
       [\c!frameoffset=\@@oioffset,\c!offset=\v!overlay,#2]
       {$\mframedstyle#3$}%
   \else
     \inframed
       [#2]
       {$\mframedstyle#3$}%
   \fi
   \endgroup}

\def\mframed
  {\dodoubleempty\domframed[\donetrue]}

\def\inmframed
  {\dodoubleempty\domframed[\donefalse]}

%D So instead of the rather versatile \type {\framed}, we ue
%D the \type {\mframed}.
%D
%D \startbuffer
%D \startformula
%D   x \times   \mframed{y} \times y^{z_z}
%D   x \times \inmframed{y} \times y^{z_z}
%D \stopformula
%D \stopbuffer
%D
%D \typebuffer
%D
%D \getbuffer
%D
%D However, we got into troubles when we want to nest sub- and
%D superscripts, like in
%D
%D \startbuffer
%D \startformula
%D   x \times \mframed{y} \times y^{\mframed{z}_{\mframed{z}}}
%D \stopformula
%D \stopbuffer
%D
%D \typebuffer
%D
%D \getbuffer
%D
%D Therefore, we can best use \type {\super} and \type {\suber}
%D instead of \type {^} and \type {_}. Both commands take care
%D of proper font switching.
%D
%D \startbuffer
%D \startformula
%D   x \times \mframed{y} \times y\super{\mframed{z}\suber{\mframed{z}}}
%D \stopformula
%D \stopbuffer
%D
%D \typebuffer
%D
%D \getbuffer
%D
%D As usual, one can specify in what way the text should be
%D framed. One should be aware of the fact that, inorder to
%D preserve the proper spacing, the \type {offset} is set to
%D \type {overlay} and \type {frameoffset} is used used
%D instead.
%D
%D \startbuffer
%D \startformula
%D   x \times y\super{\mframed[framecolor=red]{z}\suber{z}}
%D \stopformula
%D \stopbuffer
%D
%D \typebuffer
%D
%D \getbuffer
%D
%D For inline use, we also provide the \type {\inmframed}
%D alternative: we want $x \times \inmframed{y}$ in inline
%D math, right?

%D This previous framing macros needs a lot of alternatives for
%D putting rules around boxes, inserting offsets and aligning
%D text. Each step is handled by separate macros.

\def\dowidenframebox#1%
  {\setbox\framebox\vbox
     {\kern#1\hbox{\kern#1\box\framebox\kern#1}\kern#1}}

\def\dooffsetframebox{\dowidenframebox\localoffset}
\def\dolocateframebox{\dowidenframebox\ruledlinewidth}

%D Let's hope that the next few examples show us enough of
%D what needs to be done by the auxiliary macros.
%D
%D \startbuffer
%D \framed[height=1cm,offset=.5cm]   {rule based learning}
%D \framed[height=1cm,offset=0cm]    {rule based learning}
%D \framed[height=1cm,offset=none]   {rule based learning}
%D \framed[height=1cm,offset=overlay]{rule based learning}
%D \stopbuffer
%D
%D \typebuffer
%D
%D \startlinecorrection
%D \hbox{\getbuffer}
%D \stoplinecorrection
%D
%D \startbuffer
%D \framed[offset=.5cm]   {rule based learning}
%D \framed[offset=0cm]    {rule based learning}
%D \framed[offset=none]   {rule based learning}
%D \framed[offset=overlay]{rule based learning}
%D \stopbuffer
%D
%D \typebuffer
%D
%D \startlinecorrection
%D \hbox{\getbuffer}
%D \stoplinecorrection
%D
%D \startbuffer
%D \framed[strut=nee,offset=.5cm]   {rule based learning}
%D \framed[strut=nee,offset=0cm]    {rule based learning}
%D \framed[strut=nee,offset=none]   {rule based learning}
%D \framed[strut=nee,offset=overlay]{rule based learning}
%D \stopbuffer
%D
%D \typebuffer
%D
%D \startlinecorrection
%D \hbox{\getbuffer}
%D \stoplinecorrection
%D
%D \startbuffer
%D \framed[width=3cm,align=left]      {rule\\based\\learning}
%D \framed[width=3cm,align=middle]     {rule\\based\\learning}
%D \framed[width=3cm,align=right]     {rule\\based\\learning}
%D \framed[width=fit,align=middle] {rule\\based\\learning}
%D \stopbuffer
%D
%D \typebuffer
%D
%D \startlinecorrection
%D \hbox{\dontcomplain\getbuffer}
%D \stoplinecorrection
%D
%D So now we're ready for the complicated stuff. We distinguish
%D between borders with straight lines and those with round
%D corners. When using the first alternative it is possible to
%D turn off one or more lines. More fancy shapes are also
%D possible by specifying dedicated backgrounds. Turning lines
%D on and off is implemented as efficient as possible and as a
%D result is interface language dependant. This next
%D implementation evolved from simpler ones. It puts for
%D instance the rules on top of the content and provides
%D additional offset capabilities. The lot of calls to other
%D macros makes this mechanism not that easy to comprehend.

%D Getting the backgrounds right takes less code. Again we
%D have to take care of additional offsets.

\def\dobackedbox
  {\doifelsevalue{\@@framed\c!backgroundoffset}\v!frame % new
     {\dobackgroundbox\c!frameoffset}
     {\dobackgroundbox\c!backgroundoffset}}

%D We handle left, right or middle alignment as well as fixed
%D or free widths and heights. Each combination gets its own
%D macro.

%D The following code handles one-liners: \type{align={line,flushright}}.
%D Beware, since we entered a group and either or not grab the next
%D bgroup token, we need to finish the group in the oneliner mode.

\ifx\raggedoneliner\undefined \chardef\raggedoneliner\zerocount \fi

\def\doformatonelinerbox % beware: assumes explicit preceding bgroup
  {\ifcase\raggedoneliner
     \expandafter\nodoformatonelinerbox
   \else
     \expandafter\dodoformatonelinerbox
   \fi}

\def\dodoformatonelinerbox
  {\dowithnextboxcontent
     {\ignorespaces}
     {\hbox to \hsize
        {\ifcase\raggedstatus\or\hss\or\hss\fi
         \unhbox\nextbox \removeunwantedspaces
         \ifcase\raggedstatus\or    \or\hss\or\hss\fi}%
      \egroup}
     \hbox}

\def\nodoformatonelinerbox % grabs {
  {\let\next=}

%D The handlers:

\def\doformatboxSomeFormat
  {\vbox to \!!framedheight
     \bgroup
       \let\postprocessframebox\relax
       \forgetall
       \oninterlineskip
       \hsize\!!framedwidth
       \vsize\!!framedheight
       \doframedsetups
       \raggedcommand
       \dobeforeframedbox
       \bgroup
       \localbegstrut
       \aftergroup\localendstrut
       \aftergroup\doafterframedbox
       \aftergroup\egroup
       \doformatonelinerbox}

\def\doformatboxNoFormat
  {\vbox to \!!framedheight
     \bgroup
       \let\postprocessframebox\relax
       \forgetall
       \oninterlineskip
       \hsize\!!framedwidth
       \vsize\!!framedheight
       \doframedsetups
       \raggedcenter
       \vss
       \bgroup
       \localbegstrut
       \aftergroup\localendstrut
       \aftergroup\vss
       \aftergroup\egroup
       \doformatonelinerbox}

\def\doformatboxHeight
  {\vbox to \!!framedheight
     \bgroup
       \let\postprocessframebox\relax
       \forgetall
       \oninterlineskip
       \doframedsetups
       \raggedcommand
       \vss
       \bgroup
       \aftergroup\localendstrut
       \aftergroup\vss
       \aftergroup\egroup
       \localbegstrut
       \doformatonelinerbox}

\def\doformatboxWidth
  {\vbox
     \bgroup
       \let\postprocessframebox\relax
       \forgetall
       \oninterlineskip
       \hsize\!!framedwidth
       \doframedsetups
       \raggedcommand
       \dobeforeframedbox
       \bgroup
       \localbegstrut
       \aftergroup\localendstrut
       \aftergroup\doafterframedbox
       \aftergroup\egroup
       \doformatonelinerbox}

\def\doformatboxVSize
  {\vbox to \!!framedheight
     \bgroup
       \let\postprocessframebox\relax
       \forgetall
       \vsize\!!framedheight
       \doframedsetups
       \vss
       \bgroup
       \aftergroup\vss
       \aftergroup\egroup
       \hbox
         \bgroup
         \aftergroup\egroup
         \localstrut
         \doformatonelinerbox}

\def\doformatboxHSize
  {\hbox to \!!framedwidth
     \bgroup
       \let\postprocessframebox\relax
       \forgetall
       \doframedsetups
       \hss
       \localstrut
       \bgroup
       \aftergroup\hss
       \aftergroup\egroup
       \doformatonelinerbox}

\def\doformatboxNoSize
  {\hbox
     \bgroup
       \let\postprocessframebox\relax
       \doframedsetups
       \localstrut
       \doformatonelinerbox}

\let\doframedsetups\relax

%D On the next page we show some examples of how these macros
%D come into action. The examples show us how
%D \type {fit}, \type {broad} dimensions influence the
%D formatting. Watch the visualized struts. \footnote {Here we
%D used \type {\showstruts}.}
%D
%D \startpostponing
%D \bgroup
%D \showstruts
%D \dontcomplain
%D \startlinecorrection
%D \halign{#\enskip&#\enskip&#\enskip&#\enskip&#\enskip&#\cr
%D   \framed[width=.2\hsize, height=.2\hsize, align=]      {a\par b\par c}&
%D   \framed[width=.2\hsize, height=broad,    align=]      {a\par b\par c}&
%D   \framed[width=.2\hsize, height=fit,      align=]      {a\par b\par c}&
%D   \framed[width=fit,      height=.2\hsize, align=]      {a\par b\par c}&
%D   \framed[width=fit,      height=broad,    align=]      {a\par b\par c}&
%D   \framed[width=fit,      height=fit,      align=]      {a\par b\par c}\cr
%D   \noalign{\vskip1em}
%D   \framed[width=.2\hsize, height=.2\hsize, align=yes]   {a\par b\par c}&
%D   \framed[width=.2\hsize, height=broad,    align=yes]   {a\par b\par c}&
%D   \framed[width=.2\hsize, height=fit,      align=yes]   {a\par b\par c}&
%D   \framed[width=fit,      height=.2\hsize, align=yes]   {a\par b\par c}&
%D   \framed[width=fit,      height=broad,    align=yes]   {a\par b\par c}&
%D   \framed[width=fit,      height=fit,      align=yes]   {a\par b\par c}\cr
%D   \noalign{\vskip1em}
%D   \framed[width=.2\hsize, height=.2\hsize, align=right] {a\par b\par c}&
%D   \framed[width=.2\hsize, height=broad,    align=right] {a\par b\par c}&
%D   \framed[width=.2\hsize, height=fit,      align=right] {a\par b\par c}&
%D   \framed[width=fit,      height=.2\hsize, align=right] {a\par b\par c}&
%D   \framed[width=fit,      height=broad,    align=right] {a\par b\par c}&
%D   \framed[width=fit,      height=fit,      align=right] {a\par b\par c}\cr
%D   \noalign{\vskip1em}
%D   \framed[width=.2\hsize, height=.2\hsize, align=left]  {a\par b\par c}&
%D   \framed[width=.2\hsize, height=broad,    align=left]  {a\par b\par c}&
%D   \framed[width=.2\hsize, height=fit,      align=left]  {a\par b\par c}&
%D   \framed[width=fit,      height=.2\hsize, align=left]  {a\par b\par c}&
%D   \framed[width=fit,      height=broad,    align=left]  {a\par b\par c}&
%D   \framed[width=fit,      height=fit,      align=left]  {a\par b\par c}\cr
%D   \noalign{\vskip1em}
%D   \framed[width=.2\hsize, height=.2\hsize, align=middle] {a\par b\par c}&
%D   \framed[width=.2\hsize, height=broad,    align=middle] {a\par b\par c}&
%D   \framed[width=.2\hsize, height=fit,      align=middle] {a\par b\par c}&
%D   \framed[width=fit,      height=.2\hsize, align=middle] {a\par b\par c}&
%D   \framed[width=fit,      height=broad,    align=middle] {a\par b\par c}&
%D   \framed[width=fit,      height=fit,      align=middle] {a\par b\par c}\cr}
%D \stoplinecorrection
%D \blank[2*big]
%D \egroup
%D \stoppostponing

%D \macros
%D   {framednoflines, framedlastlength}
%D
%D It is possible to let the frame macro calculate the width
%D of a centered box automatically (\type {fit}). When
%D doing so, we need to reshape the box:

\newcount\framednoflines
\newdimen\framedlastlength

\def\resetshapeframebox
  {\framednoflines  \zerocount
   \framedlastlength\zeropoint}

% The next implementation is frozen! It preserves the depth,
% otherwise we get problems with framed display math and auto
% width.

% \def\doreshapeframedbox % frozen
%   {\beginofshapebox
%    \unvcopy\framebox
%    \endofshapebox
%    \global\@@globalwidth\zeropoint
%    \resetshapeframebox
%    \reshapebox
%      {\setbox0\hbox
%         {\strut\ifhbox\shapebox\unhbox\else\box\fi\shapebox}%
%       \global\advance\framednoflines \plusone
%       \ifdim\framedlastlength>\zeropoint\else
%         \global\framedlastlength\wd0
%       \fi
%       \ifdim\wd0>\@@globalwidth
%         \global\@@globalwidth\wd0
%       \fi}%
%    \dosetraggedcommand\localformat
%    \raggedcommand
%    \ifboxhasheight
%      \setbox\framebox\vbox to \localheight
%        {\hsize\@@globalwidth
%         \reshapebox{\hbox to \hsize{\ifhbox\shapebox\unhbox\else\box\fi\shapebox}}%
%         \dobeforeframedbox
%         \innerflushshapebox
%         \doafterframedbox}%
%    \else
%      \scratchdimen\dp\framebox
%      \setbox\framebox\vbox to \ht\framebox
%        {\hsize\@@globalwidth
%         \reshapebox{\hbox to \hsize{\ifhbox\shapebox\unhbox\else\box\fi\shapebox}}%
%         \innerflushshapebox}%
%      \dp\framebox\scratchdimen % \strutdp otherwise problem with math
%    \fi
%    \ifdim\framedlastlength=\zeropoint\global\framednoflines\wd\framebox\fi
%    \ifcase\framednoflines\global\framednoflines\plusone\fi}

\chardef\reshapeframeboxmethod\plusone % 0=no flush, 1=old method 2=no depth messing

\let\framedboxwidth \!!zeropoint
\let\framedboxheight\!!zeropoint
\let\framedboxdepth \!!zeropoint

\def\doreshapeframedbox % frozen
  {\beginofshapebox
   \unvcopy\framebox
   \endofshapebox
   \edef\framedboxwidth {\the\wd\framebox}%
   \edef\framedboxheight{\the\ht\framebox}%
   \edef\framedboxdepth {\the\dp\framebox}%
   \global\@@globalwidth\zeropoint
   \resetshapeframebox
   \reshapebox
     {\setbox0\hbox
        {\strut\ifhbox\shapebox\unhbox\else\box\fi\shapebox}%
      \global\advance\framednoflines \plusone
      \ifdim\framedlastlength>\zeropoint\else
        \global\framedlastlength\wd0
      \fi
      \ifdim\wd0>\@@globalwidth
        \global\@@globalwidth\wd0
      \fi}%
   \ifreshapingfailed
      % no need for anothr pass or finalizer
   \else
      \dosetraggedcommand\localformat
      \raggedcommand
      \ifboxhasheight
        \setbox\framebox\vbox to \localheight
          {\hsize\@@globalwidth
           \reshapebox{\hbox to \hsize{\ifhbox\shapebox\unhbox\else\box\fi\shapebox}}%
           \dobeforeframedbox
           \innerflushshapebox
           \doafterframedbox}%
      \else
        \setbox\framebox\vbox to \ht\framebox
          {\hsize\@@globalwidth
           \reshapebox{\hbox to \hsize{\ifhbox\shapebox\unhbox\else\box\fi\shapebox}}%
           \ifcase\reshapeframeboxmethod
             \or \innerflushshapebox \or \innerflushshapebox
           \fi}%
        \ifcase\reshapeframeboxmethod \or
          \dp\framebox\framedboxdepth % \strutdp otherwise problem with math
        \fi
      \fi
      \ifdim\framedlastlength=\zeropoint\global\framednoflines\wd\framebox\fi
      \ifcase\framednoflines\global\framednoflines\plusone\fi
   \fi}

%D The two variables \type {\framednoflines} and \type
%D {\framedlastlength} can be used in a second pass to
%D optimized framed material.

% torture test / strange case (much depth) / method 2 needed
%
% \startTEXpage[frame=on]
% \startformula \startalign \NC A \NC B \NR \intertext{test} \NC C \NC D \NR \stopalign \stopformula
% test outside formula
% \startformula \startalign \NC A \NC B \NR \intertext{test} \NC C \NC D \NR \stopalign \stopformula
% \blank[big]
% \startformula \startalign \NC \int_01 \NC B \NR \intertext{test} \NC \int_01 \NC D \NR \stopalign \stopformula
% test outside formula
% \startformula \startalign \NC \int_01 \NC B \NR \intertext{test} \NC \int_01 \NC D \NR \stopalign \stopformula
% \stopTEXpage

%D The examples on the next page show how one can give the
%D frame as well as the background an additional offset and
%D even a bit more depth. The blue outline is the frame, the
%D red box is the background and the small black outline is the
%D visualization of the resulting box, that is, we applied
%D \type{\ruledhbox} to the result.

%D \startpostponing
%D \bgroup
%D \unprotect
%D \dontcomplain
%D
%D \startbuffer
%D \vbox to \vsize
%D   \bgroup
%D     \startalignment[middle]
%D     \vss
%D     \leavevmode\vbox to .8\vsize
%D       \bgroup
%D         \hsize=300pt
%D         \setupframed
%D           [background=color,
%D            backgroundcolorachtergrondkleur=darkred,
%D            width=300pt,
%D            height=60pt,
%D            framecolorkaderkleur=DemoBlue,
%D            rulethickness=2pt]
%D         \def\status%
%D           {backgroundoffset=\framedparameter\c!backgroundoffset\\
%D            frameoffset=\framedparameter\c!frameoffset\\
%D            depth=\framedparameter\c!depth}
%D         \leavevmode \ruledhbox{\framed[backgroundoffset=0pt,frameoffset=0pt]{\status}}
%D         \vss
%D         \leavevmode \ruledhbox{\framed[backgroundoffset=5pt,frameoffset=0pt]{\status}}
%D         \vss
%D         \leavevmode \ruledhbox{\framed[backgroundoffset=0pt,frameoffset=5pt]{\status}}
%D         \vss
%D         \leavevmode \ruledhbox{\framed[backgroundoffset=2pt,frameoffset=5pt]{\status}}
%D         \vss
%D         \leavevmode \ruledhbox{\framed[backgroundoffset=5pt,frameoffset=2pt]{\status}}
%D         \vss
%D         \leavevmode \ruledhbox{\framed[backgroundoffset=5pt,frameoffset=5pt]{\status}}
%D       \egroup
%D     \vss
%D     \stopalignment
%D     \egroup
%D \stopbuffer
%D
%D \getbuffer \page
%D
%D {\setupframed[depth=4pt]\getbuffer} \page
%D
%D \protect
%D \egroup
%D \stoppostponing

%D When typesetting the framed box inline, we have to keep the
%D baseline intact outside as well as inside the framed box.

\def\doinlineframedbox
  {\scratchdimen\dimexpr\strutdp+\ruledlinewidth\relax
   \ifboxhasoffset
     \advance\scratchdimen \framedparameter\c!offset
   \fi
   \setbox\framebox\hbox{\lower\scratchdimen\box\framebox}%
   \ht\framebox\strutht
   \dp\framebox\strutdp
   \box\framebox}

%D We can also lower the box over the natural depth of the
%D line.

\def\doloweredframedbox
  {\ht\framebox\dimexpr\ht\framebox+\dp\framebox-\strutdp\relax
   \dp\framebox\strutdp
   \box\framebox}

%D Hanging the content is mainly meant for cases like the
%D following:
%D
%D \starttyping
%D \framed[strut=no]
%D   {\framed[height=2cm,location=hanging]{test}%
%D    \framed[height=1cm,location=hanging]{test}}
%D \stoptyping

\def\dohangingframedbox % best with strut=no
  {\scratchdimen\dimexpr\ht\framebox+\dp\framebox\relax
   \ht\framebox\zeropoint
   \dp\framebox\scratchdimen}

%D We can draw lines from left to right and top to bottom by
%D using the normal \type{\hairline} command. Both directions
%D need a different treatment.
%D
%D \startbuffer
%D \framed[width=4cm]           {alfa\hairline beta\hairline gamma}
%D \framed[height=2cm]            {alfa\hairline beta\hairline gamma}
%D \framed[width=4cm,height=2cm]{alfa\hairline beta\hairline gamma}
%D \stopbuffer
%D
%D \typebuffer
%D
%D \startlinecorrection
%D \hbox{\getbuffer}
%D \stoplinecorrection
%D
%D These macros try to adapt their behaviour as good as
%D possible to the circumstances and act as natural as
%D possible.

\def\vboxedhairline
  {\bgroup
   \dimen2=\ifboxhasoffset \localoffset \else \zeropoint \fi
   \dimen4=\dimexpr\dimen2+\ruledlinewidth\relax
   \setbox0\vbox
     {\advance\hsize 2\dimen4
      \vskip\dimen2
      \hrule
        \!!height\ruledlinewidth
        \!!depth\zeropoint
        \!!width\hsize
      \vskip\dimen2}%
  %\endgraf\nointerlineskip\endgraf
  %\moveleft\dimen4\box0
  %\endgraf\nointerlineskip\localbegstrut
   \endgraf\obeydepth\nointerlineskip
   \moveleft\dimen4\box0
   \endgraf\nointerlineskip\localbegstrut
   \egroup}

\def\hboxedhairline % use framed dimen
  {\bgroup
   \dimen2=\ifboxhasoffset \localoffset \else \zeropoint \fi
   \ifboxhasheight
     \dimen4\dimexpr\localheight/2+\strutdp-2\ruledlinewidth\relax
     \dimen6\dimexpr\localheight/2-\strutdp+2\ruledlinewidth\relax
   \else
     \dimen4\dimexpr\strutht+\dimen2\relax
     \dimen6\dimexpr\strutdp+\dimen2\relax
   \fi
   \unskip
   \setbox\scratchbox\hbox
     {\hskip\dimen2
      \vrule\!!height\dimen4\!!depth\dimen6\!!width\ruledlinewidth
      \hskip\dimen2}%
   \ht\scratchbox\strutht
   \dp\scratchbox\strutdp
   \box\scratchbox
   \ignorespaces
   \egroup}

%D The argument of the frame command accepts \type{\\} as a
%D sort of newline signal. In horizontal boxes it expands to a
%D space.

\def\vboxednewline
  {\endgraf\ignorespaces}

\def\hboxednewline
  {\unskip\normalspace\ignorespaces}

%D We can set each rule on or off. The default setting is
%D inherited from \type{frame}. An earlier implementation
%D use a bit different approach, but the new one seems more
%D natural:
%D
%D \bgroup
%D \setuptyping[margin=0pt]
%D \startlinecorrection
%D \startbuffer
%D \framed[offset=overlay,frame=on]{\darkred\blackrule}
%D \stopbuffer
%D \hbox{\getbuffer\vbox{\typebuffer}}
%D
%D \startbuffer
%D \framed[offset=overlay,frame=on,bottomframe=off]{\darkred\blackrule}
%D \stopbuffer
%D \hbox{\getbuffer\vbox{\typebuffer}}
%D
%D \startbuffer
%D \framed[offset=overlay,frame=on,bottomframe=on]{\darkred\blackrule}
%D \stopbuffer
%D \hbox{\getbuffer\vbox{\typebuffer}}
%D
%D \startbuffer
%D \framed[offset=overlay,frame=off]{\darkred\blackrule}
%D \stopbuffer
%D \hbox{\getbuffer\vbox{\typebuffer}}
%D
%D \startbuffer
%D \framed[offset=overlay,frame=off,bottomframe=off]{\darkred\blackrule}
%D \stopbuffer
%D \hbox{\getbuffer\vbox{\typebuffer}}
%D
%D \startbuffer
%D \framed[offset=overlay,frame=off,bottomframe=on]{\darkred\blackrule}
%D \stopbuffer
%D \hbox{\getbuffer\vbox{\typebuffer}}
%D \stoplinecorrection
%D \egroup

%D \macros
%D   {setupblackrules}
%D
%D The graphic capabilities of \TEX\ do not go beyond simple
%D filled rules, except of course when using specials. Let's
%D start with a warning: using this commands is far more slower
%D than using the \TEX\ primitives \type{\hrule} and
%D \type{\vrule}, but they save us some tokens. The
%D characteristics of these rule drawing command can be set by:
%D
%D \showsetup{setupblackrules}

\def\setupblackrules
  {\dodoubleargument\getparameters[\??bj]}

%D \macros
%D   {blackrule}
%D
%D The simple command draws only one rule. Its optional
%D argument can be used to specify the dimensions. By setting
%D the width, height or depth to \type {max}, one gets the
%D natural dimensions.
%D
%D \showsetup{blackrule}

\def\doblackrule[#1]%
  {\hbox\bgroup
   \getparameters[\??bj][#1]%
   \setstrut
   \doif\@@bjwidth \v!max{\def\@@bjwidth {1em}}%
   \doif\@@bjheight\v!max{\def\@@bjheight{\strutht}}%
   \doif\@@bjdepth \v!max{\def\@@bjdepth {\strutdp}}%
   \localstartcolor[\@@bjcolor]%
     \vrule
       \!!width \@@bjwidth
       \!!height\@@bjheight
       \!!depth \@@bjdepth
   \localstopcolor
   \egroup}

\unexpanded\def\blackrule
  {\dosingleempty\doblackrule}

%D \macros
%D   {blackrules}
%D
%D One can call for a sequence of black rules, if needed
%D equally spaced over the given width.
%D
%D \showsetup{blackrules}
%D
%D The two alternative calls are therefore:
%D
%D \startbuffer
%D Tell me, is this according to the \blackrules[n=6]?
%D These \blackrules[alternativevariant=b,n=10,distance=.2em,width=4cm] are quite clear.
%D \stopbuffer
%D
%D \typebuffer
%D
%D or:
%D
%D \startvoorbeeld
%D \startlines
%D \getbuffer
%D \stoplines
%D \stopvoorbeeld
%D
%D We could of course have implemented this macro using
%D \type{\leaders}, but this would probably have taken more
%D tokens.

\def\doblackrules[#1]%
  {\hbox\bgroup
   \getparameters[\??bj][#1]%
   \!!widtha\@@bjwidth
   \!!widthb\@@bjdistance
   \doif\@@bjalternative\c!b
     {\scratchcounter\@@bjn
      \ifnum\scratchcounter=\plusone
        \!!widthb\zeropoint
      \else
        \advance\scratchcounter \minusone
        \advance\!!widtha -\scratchcounter\!!widthb
        \divide \!!widtha \@@bjn
      \fi}%
   \localstartcolor[\@@bjcolor]%
     \dorecurse\@@bjn
       {\vrule
          \!!width \!!widtha
          \!!height\@@bjheight
          \!!depth \@@bjdepth
        \hskip\!!widthb}%
     \unskip
   \localstopcolor
   \egroup}

\unexpanded\def\blackrules
  {\dosingleempty\doblackrules}

%D The next commands can be used to draw margin rules. We
%D support two methods: \marginrule{one for in||line use} and
%D one that acts on a paragraph. Drawing a margin rule is
%D rather straightforward because we can use the commands that
%D put text in the margin.

\def\dodrawmarginrule
  {\setbox\scratchbox\hbox
     {\vrule\!!depth\strutdepth\!!height\strutheight\!!width\@@karulethickness}%
   \smashbox\scratchbox % no \vsmash !!!
   \box\scratchbox}

\def\drawmarginrule
  {\strut\inleft{\dodrawmarginrule}}

%D \macros
%D   {marginrule}
%D
%D The first method gobbles words and simply puts a bar in the
%D margin. This method is not entirely robust.
%D
%D \showsetup{marginrule}

\definecomplexorsimple\marginrule

\def\simplemarginrule
  {\let\processword\drawmarginrule
   \processwords}

\def\complexmarginrule[#1]%
  {\ifnum#1<\@@kalevel\relax \else
     \def\@@kadefaultwidth{#1}%
     \expandafter\simplemarginrule
   \fi}

%D We need an auxiliary variable

\def\@@kadefaultwidth{1}

%D \macros
%D   {setupmarginrules}
%D
%D This macro definitions show us that we can pass an optional
%D level, which is matched against the previous set one. The
%D level can be set up with
%D
%D \showsetup{setupmarginrules}

\def\setupmarginrules
  {\dodoubleargument\getparameters[\??ka]}

%D \macros
%D   {startmarginrule}
%D
%D The second method collects text and reformats it afterwards,
%D using the shapebox macros. We prevent local margin rules.
%D
%D \showsetup{startmarginrule}

\definecomplexorsimple\startmarginrule

\def\simplestartmarginrule
  {\bgroup
   \let\drawmarginrule\relax
   \let\stopmarginrule\dostopmarginrule
   \beginofshapebox}

\def\complexstartmarginrule[#1]%
  {\bgroup
   \let\drawmarginrule\relax
   \ifnum#1<\@@kalevel\relax
     \let\stopmarginrule\egroup
   \else
     \def\@@kadefaultwidth{#1}%
     \let\stopmarginrule\dostopmarginrule
     \expandafter\beginofshapebox
   \fi}

\def\dostopmarginrule
  {\endofshapebox
   \reshapebox
     {\hbox{\inleftmargin{\dodrawmarginrule}\box\shapebox}}%
   \flushshapebox
   \egroup}

%D \startbuffer
%D \setupmarginrules[level=5]
%D
%D \startmarginrule[1]
%D First we set the level at~5. Next we typeset this first
%D paragraph as a level~1 one. As expected no rule show up.
%D \stopmarginrule
%D
%D \startmarginrule[5]
%D The second paragraph is a level~5 one. As we can see here,
%D the marginal rule gets a width according to its level.
%D \stopmarginrule
%D
%D \startmarginrule[8]
%D It will of course be no surprise that this third paragraph
%D has a even thicker margin rule. This behavior can be
%D overruled by specifying the width explictly.
%D \stopmarginrule
%D \stopbuffer
%D
%D In next example we show most features. Watch the rule
%D thickness adapting itself to the level.
%D
%D \startvoorbeeld
%D \getbuffer
%D \stopvoorbeeld
%D
%D We just said:
%D
%D \typebuffer

%D \macros
%D   {vl, hl}
%D
%D The command \type{\vl} draws a vertical rule \vl\ with strut
%D dimensions, multiplied with the factor specified in the
%D optional argument. The height and depth are clipped \vl[3]
%D to the baselinedistance. Its horizontal counterpart
%D \type{\hl} draws a horizontal rule \hl\ with a width of 1em,
%D multiplied with the optional factor. The horizontal rule is
%D drawn on top of the baseline.
%D
%D \showsetup{vl}
%D \showsetup{hl}

\def\complexvl[#1]%
  {\bgroup
   \!!dimena#1\strutht
   \!!dimenb#1\strutdp
   \setbox\scratchbox\hbox
     {\vrule
        \!!width \linewidth
        \!!height\!!dimena
        \!!depth \!!dimenb}%
   \dp\scratchbox\strutdp
   \ht\scratchbox\strutht
   \box\scratchbox
   \egroup}

\def\complexhl[#1]%
  {\hbox
     {\vrule
        \!!width #1\s!em
        \!!height\linewidth
        \!!depth \zeropoint}}

\definecomplexorsimple\vl \def\simplevl{\complexvl[1]}
\definecomplexorsimple\hl \def\simplehl{\complexhl[1]}

%D \macros
%D   {hairline, thinrule, thinrules, setupthinrules}
%D
%D Drawing thin lines can of course easily be accomplished by
%D the \TEX\ primitives \type{\hrule} and \type{\vrule}. The
%D next few macros however free us from some specifications.
%D
%D \startbuffer
%D some text
%D
%D \hairline
%D
%D some more text
%D
%D \thinrule
%D
%D more and more text
%D
%D hi \thinrule\ there
%D
%D and then the final text
%D \stopbuffer
%D
%D \typebuffer
%D
%D becomes
%D
%D \startvoorbeeld
%D \getbuffer
%D \stopvoorbeeld
%D
%D So we've got
%D
%D \showsetup{hairline}
%D \showsetup{thinrule}
%D
%D Both can be set up with:
%D
%D \showsetup{setupthinrules}
%D
%D We also have
%D
%D \showsetup{thinrules}
%D
%D which looks like: \thinrules[n=2]

\def\thinrule
  {\strut
   \bgroup
   \chardef\ruletype\plusone
   \processaction
     [\@@dlalternative]
     [ \v!a=>\chardef\ruletype0,% no line
      %\v!b=>\chardef\ruletype1,% height/depth
       \v!c=>\chardef\ruletype2,% topheight/botdepth
      %  11=>\chardef\ruletype1,% fallback for  backgrounds
          0=>\chardef\ruletype0,% compatible with backgrounds
      %   1=>\chardef\ruletype1,% compatible with backgrounds
          2=>\chardef\ruletype2]% compatible with backgrounds
   \doifsomething\@@dlrulethickness
     {\linewidth\@@dlrulethickness}%
   \ifdim\linewidth=\zeropoint
     \chardef\ruletype\zerocount
   \else
     \doifnot\@@dlframe\v!on{\chardef\ruletype\zerocount}%
   \fi
   \ifnum\ruletype=\plusone
     \doif\@@dlheight\v!max{\let\@@dlheight\!!plusone}%
     \doif\@@dldepth \v!max{\let\@@dldepth \!!plusone}%
   \else
     \let\@@dlheight\!!plusone
     \let\@@dldepth\!!plusone
   \fi
   \freezedimensionwithunit\@@dlheight\strutht
   \freezedimensionwithunit\@@dldepth\strutdp
   \divide\linewidth \plustwo
   \doifelse\@@dlbackground\v!color
     {\startcolor[\@@dlbackgroundcolor]%
      \ifnum\ruletype=\plustwo % prevent overshoot due to rounding
        \leaders
          \hrule
            \!!height\dimexpr\@@dlheight-.5\linewidth\relax
            \!!depth \dimexpr\@@dldepth -.5\linewidth\relax
          \hfill
      \else
        \leaders
          \hrule
            \!!height\@@dlheight
            \!!depth \@@dldepth
          \hfill
      \fi
      \stopcolor
      \ifcase\ruletype
        % no rule
      \or
        \startcolor[\@@dlcolor]%
        \hfillneg
        \leaders\hrule\!!height\linewidth\!!depth\linewidth\hfill
        \stopcolor
      \or
        \startcolor[\@@dlcolor]%
        \hfillneg\leaders\hrule\!!height\dimexpr-\@@dldepth+\linewidth\relax\!!depth\@@dldepth\hfill
        \hfillneg\leaders\hrule\!!height\@@dlheight\!!depth\dimexpr-\@@dlheight+\linewidth\relax\hfill
        \stopcolor
      \fi}
     {\ifcase\ruletype \else
        \startcolor[\@@dlcolor]%
        \leaders\hrule\!!height\@@dlheight\!!depth\@@dldepth\hfill
        \stopcolor
      \fi}%
   \strut
   \carryoverpar\egroup}

\def\hairline
  {\endgraf
   \thinrule
   \endgraf}

\def\dosetupthinrules[#1]%
  {\getparameters[\??dl][#1]}

\def\setupthinrules
  {\dosingleargument\dosetupthinrules}

\def\dothinrules[#1]%
  {\bgroup
   \dosetupthinrules[#1]%
   \@@dlbefore
   \assignvalue\@@dlinterlinespace\@@dlinterlinespace{1.0}{1.5}{2.0}%
   \spacing\@@dlinterlinespace
   \dorecurse\@@dln
     {\ifnum\recurselevel=\@@dln \dothinrulesnobreak \else
      \ifnum\recurselevel=2      \dothinrulesnobreak \fi\fi
                                 \thinrule
      \ifnum\recurselevel<\@@dln\relax
        % test needed, else messed up whitespace
        \ifx\@@dlinbetween\empty
          \softbreak
        \else
          \endgraf
          \nowhitespace
          \@@dlinbetween
        \fi
      \fi}%
   \doifelsenothing\@@dlafter
     {\carryoverpar\egroup}
     {\@@dlafter\egroup}}

\def\thinrules
  {\dosingleempty\dothinrules}

%D A couple of examples are given below.
%D
%D \startbuffer
%D \setupthinrules[n=3,inbetween=,color=gray]
%D
%D test test \thinrules\ test test \par
%D test test \thinrules [color=green] test test \par
%D test test \thinrules [height=max, depth=max] test test \par
%D
%D \setupthinrules[height=.9,depth=.9]
%D
%D test test \thinrules\ test test \par
%D test test \thinrules [alternativevariant=b] test test \par
%D test test \thinrules [alternativevariant=c] test test \par
%D test test \thinrules [alternativevariant=c,inbetween=\vskip2ex] test test \par
%D \stopbuffer
%D
%D \typebuffer {\getbuffer}
%D
%D There are a couple of alternative ways to visualize rules
%D using backgrounds. At first sight these may look strange,
%D but they make sense in educational settings. The
%D alternatives are more or less compatible with the more
%D advanced \METAPOST\ based implementation.
%D
%D \startbuffer[a]
%D \setupthinrules
%D   [n=2,
%D    backgroundcolor=gray  ,
%D    rulethickness=1pt,
%D    colorkleur=donkerblauw,
%D    after=\blank,
%D    before=\blank]
%D \stopbuffer
%D
%D \typebuffer[a]
%D
%D \startbuffer[b]
%D \thinrules[alternativevariant=a]
%D \thinrules[alternativevariant=b]
%D \thinrules[alternativevariant=c]
%D \stopbuffer
%D
%D \typebuffer[b] \getbuffer[a,b]
%D
%D \startbuffer[b]
%D \thinrules[alternativevariant=a,background=color]
%D \thinrules[alternativevariant=b,background=color]
%D \thinrules[alternativevariant=c,background=color]
%D \stopbuffer
%D
%D \typebuffer[b] \getbuffer[a,b]
%D
%D \startbuffer[b]
%D \thinrules[alternativevariant=a,height=.8,depth=.8,background=color]
%D \thinrules[alternativevariant=b,height=.8,depth=.8,background=color]
%D \thinrules[alternativevariant=c,height=.8,depth=.8,background=color]
%D \stopbuffer
%D
%D \typebuffer[b] \getbuffer[a,b]

%D \macros
%D   {optimizethinrules}
%D
%D By saying \type {\thinrulestrue} or \type {-false}, we
%D can influence the way dangling lines are handled.

\newif\ifoptimizethinrules \optimizethinrulestrue

\def\dothinrulesnobreak
  {\ifoptimizethinrules\penalty500\fi}

%D \macros
%D   {startframedtext, setupframedtexts, defineframedtext}
%D
%D The general framing command we discussed previously, is not
%D entirely suited for what we call framed texts, as for
%D instance used in intermezzo's. The next examples show what
%D we have in mind.
%D
%D \startbuffer[framed-0]
%D \setupframedtexts
%D   [frame=off,
%D    width=\hsize,
%D    background=screen]
%D
%D \startframedtext
%D By default the framed text is centered \dots
%D \stopframedtext
%D
%D \startframedtext[right]
%D \dots\ but we can also align left, middle and right.
%D \stopframedtext
%D \stopbuffer
%D
%D \startbuffer[framed-1]
%D \defineframedtext
%D   [Example]
%D   [width=6cm,
%D    height=5cm]
%D
%D \startExample
%D \typebuffer[framed-1]
%D \stopExample
%D \stopbuffer
%D
%D \startbuffer[framed-2]
%D \defineframedtext
%D   [Example]
%D   [width=6cm]
%D
%D \startExample
%D \typebuffer[framed-2]
%D \stopExample
%D \stopbuffer
%D
%D \startbuffer[framed-3]
%D \defineframedtext
%D   [Example]
%D   [height=5cm]
%D
%D \startExample
%D \typebuffer[framed-3]
%D \stopExample
%D \stopbuffer
%D
%D \startbuffer[framed-4]
%D \defineframedtext
%D   [Example]
%D   [width=fit,height=broad]
%D
%D \Example{a very exciting example}
%D \stopbuffer
%D
%D \bgroup \setuptyping[margin=0pt] \getbuffer[framed-0] \egroup
%D \bgroup \setuptyping[margin=0pt] \getbuffer[framed-1] \egroup
%D \bgroup \setuptyping[margin=0pt] \getbuffer[framed-2] \egroup
%D \bgroup \setuptyping[margin=0pt] \getbuffer[framed-3] \egroup
%D \bgroup \setuptyping[margin=0pt] \getbuffer[framed-4] \egroup
%D
%D Here we can see that we have a predefined framed text class
%D as well as the tools for defining our own. So we have:
%D
%D \showsetup{setupframedtexts}
%D
%D as well as the definition command:
%D
%D \showsetup{defineframedtext}
%D
%D that generates two commands:
%D
%D \showsetup{start<<framedtext>>}
%D \showsetup{<<framedtext>>}
%D
%D The next definition shows the defaults.

\def\dodefineframedtext[#1][#2]%
  {\presetlocalframed[\??kd#1]%
   \getparameters[\??kd#1]
     [\c!width=0.75\hsize,
      \c!height=\v!fit,
      \c!align=\v!yes,
      \c!top=,
      \c!bottom=\vfill,
      \c!offset=1em,
      \c!bodyfont=,
      \c!style=,
      \c!color=,
      \c!left=,
      \c!right=\hfill,
      \c!before=\blank,
      \c!after=\blank,
      \c!inner=,
      \c!frame=\v!on,
      \c!topframe=,
      \c!bottomframe=,
      \c!leftframe=,
      \c!rightframe=,
      \c!radius=.5\bodyfontsize,
      \c!corner=\v!rectangular,
      \c!foregroundcolor=,
      \c!foregroundstyle=,
      \c!background=,
      \c!backgroundcolor=,
      \c!backgroundscreen=\@@rsscreen,
      \c!linecorrection=\v!on,
      \c!depthcorrection=\v!on,
      \c!margin=\v!standard,
      \c!orientation=,
      \c!indenting=,
      #2]%
   \setvalue{\e!start#1}{\dostartframedtext[#1]}%
   \setvalue{\e!stop #1}{\dostopframedtext     }%
   \setvalue        {#1}{\doframedtext     [#1]}}

\def\defineframedtext
  {\dodoubleempty\dodefineframedtext}

%D We define the general (and original) case by just saying:

\defineframedtext[\v!framedtext]

%D We need several steps before the actual job is done,
%D because we have to handle an optional identifier (and
%D because these commands evolved out of a single case).

\def\framedtextparameter#1#2%
  {\csname\??kd#1#2\endcsname}

\def\dosetupframedtexts[#1][#2]%
  {\ifsecondargument
     \def\docommand##1{\getparameters[\??kd##1][#2]}%
     \processcommacommand[#1]\docommand % new, #1 may be macro
   \else
     \getparameters[\??kd\v!framedtext][#1]%
   \fi}

\def\setupframedtexts
  {\dodoubleempty\dosetupframedtexts}

\def\dostartframedtext
  {\bgroup\dotripleempty\dodostartframedtext}

\def\dodostartframedtext[#1][#2][#3]%
  {\doifassignmentelse{#2}
     {\dododostartframedtext[#1][][#2]}
     {\dododostartframedtext[#1][#2][#3]}}

\setfalse\framedtextlocationnone

\def\dododostartframedtext[#1][#2][#3]% #3 only passed to framed, not to framedtext
  {\doifsomething{#2}{\setvalue{\??kd#1\c!location}{#2}}% does not listen to #3
   \setfalse\framedtextlocationnone
   \processaction % \v!low en \v!depth are already taken !
     [\framedtextparameter{#1}\c!location]
     [   \v!left=>\letvalue{\??kd#1\c!left }\relax
                  \letvalue{\??kd#1\c!right}\hfill,
        \v!right=>\letvalue{\??kd#1\c!left }\hfill
                  \letvalue{\??kd#1\c!right}\relax,
       \v!middle=>\letvalue{\??kd#1\c!left }\hfill
                  \letvalue{\??kd#1\c!right}\hfill,
         \v!none=>\letvalue{\??kd#1\c!left }\relax % new
                  \letvalue{\??kd#1\c!right}\relax % new
                  \settrue\framedtextlocationnone]%
   \letvalue{\??kd#1\c!location}\empty
   % removed 06/2001
   % \forgetparindent
   % added 06/2001 [see demo-bbv]
   \localhsize\hsize \checkframedtext
   % so far
   \setbox\framebox\vbox
     \startboxedcontent
       \hsize\localhsize
    %  \insidefloattrue % ? better
       \expanded{\switchtobodyfont[\framedtextparameter{#1}\c!bodyfont]}%
       \startcolor[\framedtextparameter{#1}\c!color]%
       \localframed[\??kd#1][\c!strut=\v!no,#3]% todo: use delayedstrut
       \bgroup
         \let\\=\endgraf
         \framedtextparameter{#1}\c!inner % oud spul
         \doifvalue{\??kd#1\c!depthcorrection}\v!on % new, inside box
           {\bgroup
            \verticalstrut
            % we need \nowhitespace in case of setups setting whitespace
            % nb, not safe, text vs \vbox as next
            \vskip-\struttotal
            \nowhitespace % na vskip ! new 20/05/2004, fails with next content being box (\scale{..})
            }%
         \doinhibitblank % \blank[\v!disable]% plaatst signal
\setupindenting[\framedtextparameter{#1}\c!indenting]%
         \doconvertfont{\framedtextparameter{#1}\c!style}\empty
         \def\dostopframedtext{\dodostopframedtext{#1}{#2}}}

%D The \type {none} option is handy for nested usage, as
%D in the presentation styles, where we don't want
%D interference.

\def\dodostopframedtext#1#2% % no \baselinecorrection, see faq docs
  {\endgraf
   \removelastskip
   \doifvalue{\??kd#1\c!depthcorrection}\v!on % local and global
     {\forgetall
      \vskip-\struttotal
      \verticalstrut
      \egroup
      \forgetall
      \vskip-\lineheight
      % will be an option, not default
      % \setbaselinecorrections
      % \donegbotbaselinecorrection
      \verticalstrut}
   \stopboxedcontent
   \stopcolor
   \ifconditional\framedtextlocationnone
     \egroup
     \box\framebox
   \else\ifinsidefloat
     \egroup
     \box\framebox
   \else
     \egroup
     \doplacement[\??kd#1][\c!depthcorrection=\v!off]{\box\framebox}%
   \fi\fi
   \egroup}

%D Placement can be ignored:
%D
%D \starttyping
%D \hbox to \hsize \bgroup
%D     \startframedtext[none][width=.5\textwidth] \input tufte \stopframedtext
%D     \startframedtext[none][width=.5\textwidth] \input zapf  \stopframedtext
%D \egroup
%D
%D \hbox to \hsize \bgroup
%D     \setupframedtexts[location=none]%
%D     \startframedtext[width=.5\textwidth] \input zapf  \stopframedtext
%D     \startframedtext[width=.5\textwidth] \input tufte \stopframedtext
%D \egroup
%D \stoptyping

%D The simple brace (or group) delimited case is typeset
%D slightly different and is not aligned.

\def\doframedtext
  {\bgroup\dodoubleempty\dodoframedtext}

\def\dodoframedtext[#1][#2]% beware!
  {\expanded{\switchtobodyfont[\getvalue{\??kd#1\c!bodyfont}]}%
   \localframed[\??kd#1][\c!strut=\v!no,#2]%
     \bgroup
       \blank[\v!disable]%
       \let\\=\endgraf
       \getvalue{\??kd#1\c!inner}%   % kleur naar outer level
       \dostartattributes{\??kd#1}\c!style\c!color\empty
       \bgroup
       \aftergroup\docloseframedtext
       \let\next=}

\def\docloseframedtext
  {\removelastskip
   \dostopattributes
   \egroup
   \egroup}

%D \macros
%D   {defineframed}
%D
%D One can also define simple framed texts, using:
%D
%D \showsetup{defineframed}

\def\defineframed
  {\dodoubleempty\dodefineframed}

\def\dodefineframed[#1][#2]%
  {\iffirstargument
     \setvalue{#1}{\dodoubleempty\doframed[#2]}%
   \fi}

\def\doframed[#1][#2]%
  {\framed[#1,#2]}

%D \macros
%D   {textrule, starttextrule, setuptextrules}
%D
%D Putting rules before and after a paragraph is very space
%D sensitive, but the next command handles that quite well. It
%D comes in two disguises:
%D
%D \startbuffer
%D \textrule[top]{fragments}
%D   \input reich
%D \textrule
%D \stopbuffer
%D
%D \bgroup \typebuffer \getbuffer \egroup
%D
%D \startbuffer
%D \setuptextrules
%D   [width=90pt,distance=12pt,rulecolor=blue,
%D    bodyfont=small,style=\sc,color=red]
%D
%D \starttextrule{Ship Building Tools}
%D   \nl \setuptolerance[tolerant] \input materie
%D \stoptextrule
%D \stopbuffer
%D
%D \bgroup \typebuffer \getbuffer \egroup
%D
%D \startbuffer
%D \setuptextrules
%D   [location=inmargin,
%D    bodyfont=small,style=slantedbold]
%D
%D \starttextrule{wonderful}
%D   \input tufte
%D \stoptextrule
%D \stopbuffer
%D
%D \bgroup \typebuffer \getbuffer \egroup
%D
%D The formal definition of these commands is:
%D
%D \showsetup{textrule}
%D \showsetup{starttextrule}
%D \showsetup{setuptextrules}
%D
%D The implementation looks a bit complicated due to the
%D optional arguments.

\def\setuptextrules
  {\dodoubleargument\getparameters[\??tl]}

\def\complextextrule[#1]% if needed we can make it installable
  {\let\next\dobottomtextrule
   \processaction
     [#1]
     [    \v!top=>\let\next\dotoptextrule,
       \v!middle=>\let\next\domiddletextrule,
       \v!bottom=>\let\next\dobottomtextrule]%
   \dosinglegroupempty\next}

\definecomplexorsimple\textrule

\def\simpletextrule
  {\dosinglegroupempty\dounknowntextrule}

\def\docomplextextrule#1%
  {\bgroup
   \advance\hsize\dimexpr-\rightskip-\leftskip\relax
   \setbox\scratchbox\hbox to \hsize
     {\dimen4\dimexpr .5ex+.5\linewidth\relax
      \dimen6\dimexpr-.5ex+.5\linewidth\relax
      \doifnothing{#1}\firstargumentfalse
      \iffirstargument
        \doifelse\@@tllocation\v!inmargin
          {\llap{\doattributes\??tl\c!style\c!color{#1}\hskip\leftmargindistance}}
          {\color[\@@tlrulecolor]
             {\vrule\!!height\dimen4\!!depth\dimen6\!!width\@@tlwidth}%
           \hbox spread 2\dimexpr\@@tldistance\relax
             {\hss\doattributes\??tl\c!style\c!color{\strut#1}\hss}}%
      \fi
      \color[\@@tlrulecolor]
        {\leaders\hrule\!!height\dimen4\!!depth\dimen6\hfill}}%
   \ht\scratchbox\strutht
   \dp\scratchbox\strutdp
   \noindent\box\scratchbox
%\nobreak\verticalstrut\kern-\struttotal
% evt \witruimte
   \egroup}

\def\dotoptextrule#1%
  {\page[\v!preference] % interferes
   %\whitespace % no
   \@@tlbefore
   \docomplextextrule{#1}%
% todo, option: \doifnothing{#1}{\ruledvskip-.5ex}
   \nowhitespace
   \@@tlinbetween
   \endgraf}

\def\dodobottomtextrule#1#2%
  {\ifhmode
     \endgraf
   \fi
   \dimen0\strutdp
   \ifdim\prevdepth>\strutdp\else % was <\strutdp
     \ifdim\prevdepth>\zeropoint
       \advance\dimen0 -\prevdepth
     \fi
   \fi
   \advance\dimen0 .5ex
   \vskip\dimen0
% ==
%    \vskip\dimexpr \strutdp + .5ex
%       \ifdim\prevdepth>\strutdp\else\ifdim\prevdepth>\zeropoint-\prevdepth\fi\fi\relax
%
   \@@tlinbetween
   \doifelsenothing{#2}
     {\bgroup
      \advance\hsize\dimexpr-\rightskip-\leftskip\relax
      \nointerlineskip
      \moveleft-\leftskip\vbox
        {\color[\@@tlrulecolor]
           {\hrule\!!depth\linewidth\!!height\zeropoint\!!width\hsize}}%
      \egroup}
     {\docomplextextrule{#2}}%
   \ifvmode\prevdepth\zeropoint\fi
   #1%
   \page[\v!preference]}

\def\dobottomtextrule
  {\dodobottomtextrule\@@tlafter}

\def\domiddletextrule
  {\dodobottomtextrule\@@tlinbetween}

\def\dounknowntextrule
  {\iffirstargument
     \@EA\dotoptextrule
   \else
     \@EA\dobottomtextrule\@EA\empty
   \fi}

%D The grouped commands also supports bodyfont switching:

\def\starttextrule#1%
  {\bgroup
   \def\dounknowntextrule{\domiddletextrule}
   \dotoptextrule{#1}
   \bgroup
   \doifsomething\@@tlbodyfont{\switchtobodyfont[\@@tlbodyfont]}}

\def\stoptextrule
  {\par
   \egroup
   \dobottomtextrule\empty
   \egroup}

%D \macros
%D   {fillinrules, setupfillinrules}
%D
%D The next few commands do not really deserve a place in a
%D core module, because they deal with specific typography.
%D Nevertheless I decided to make them part of the core,
%D because they permit us to make questionaires. Let's start
%D with some examples.
%D
%D \fillinrules[n=2,width=fit]{first}
%D \fillinrules[n=2,width=broad]{first}
%D \fillinrules[n=2,width=3cm]{first}
%D \fillinrules[n=2,width=3cm,distance=.5em,separator=:]{first}
%D \fillinrules[n=2]{first}{last}
%D \fillintext{first}{last} \input reich \par
%D
%D The main command is \type{\fillinrules}. This command takes
%D one and an optional second argument and sets a paragraph with
%D empty visualized lines.
%D
%D \showsetup{fillinrules}
%D \showsetup{setupfillinrules}

\def\setupfillinrules
  {\dodoubleargument\getparameters[\??il]}

\definecomplexorsimpleempty\fillinrules

\def\complexfillinrules[#1]%
  {\def\docomplexfillinrules##1##2%
     {\dodocomplexfillinrules[#1]{##1}{##2}{\thinrules
        [\c!n=\@@iln,\c!interlinespace=\@@ilinterlinespace,\c!before=,\c!after=]}}%
   \dodoublegroupempty\docomplexfillinrules}

\def\dodocomplexfillinrules[#1]#2#3#4%
  {\endgraf
   \@@ilbefore
   \begingroup
   \setupfillinrules[#1]%
   \noindent
   \doifsomething{#2}
     {\doifelse\@@ilwidth\v!fit
        {\let\@@ildistance\!!zeropoint
         \hbox}
        {\doifelse\@@ilwidth\v!broad
           {\hbox}
           {\hbox to \@@ilwidth}}%
        \bgroup
          \doattributes\??il\c!style\c!color{\strut#2\hfill\@@ilseparator}%
          \hskip\@@ildistance
       \egroup}%
  %\hangindent=\wd0\relax % tzt hang=yes,n
  %\parindent=\hangindent
  %\box0\relax
   \setupwhitespace[\v!big]%
   \ignorespaces
   #4%
   \doifsomething{#3}
     {\kern\@@ildistance
      \doattributes\??il\c!style\c!color{#3\strut}}%
   \endgroup
   \endgraf
   \@@ilafter}

%D \macros
%D   {fillintext}
%D
%D To provide compatible layouts when texts and lines are
%D mixed, one can typeset a paragraph by using the command
%D \type{\fillintext}.
%D
%D \showsetup{fillintext}

\definecomplexorsimpleempty\fillintext

\def\complexfillintext[#1]% rather rough, using an \unhbox is suboptimal
  {\def\docomplexfillintext##1##2%
     {\dowithnextbox
        {\dodocomplexfillinrules[#1]{##1}{\hfill##2}{\unhbox\nextbox\unskip}}%
      \hbox\bgroup\let\par\egroup\ignorespaces}%
   \dodoublegroupempty\docomplexfillintext}

%D \macros
%D   {fillinline, setupfillinlines}
%D
%D Another member of the family takes care of putting a (often
%D small) rule after a piece of text, like
%D
%D \startbuffer
%D \fillinline \input reich \par
%D \fillinline[margin=0cm] \input reich \par
%D \stopbuffer
%D
%D \startvoorbeeld
%D \getbuffer
%D \stopvoorbeeld
%D
%D which was typeset by saying:
%D
%D \typebuffer
%D
%D The two commands that take care of this are:
%D
%D \showsetup{fillinline}
%D \showsetup{setupfillinlines}

\def\setupfillinlines
  {\dodoubleargument\getparameters[\??iv]}

\definecomplexorsimpleempty\fillinline

\def\complexfillinline[#1]%
  {%\endgraf % interferes with \definedescription cum suis
   \@@ivbefore
   \begingroup
   \setupfillinlines[#1]%
   \advance\rightskip \@@ivmargin
   \parfillskip\zeropoint
   \def\par % very dangerous
     {\let\par\endgraf % -)
      \ifhmode\unskip\hfill\fi
      \scratchdimen\dimexpr\@@ivwidth-\@@ivdistance\relax
      \ifdim\scratchdimen>\@@ivmargin\else\expandafter\rlap\fi
        {\kern\@@ivdistance
         \vrule
           \!!width   \scratchdimen
           \!!height.5\linewidth
           \!!depth .5\linewidth}%
      \endgraf % !
      \endgroup
      \endgraf % !
      \@@ilafter}}

%D \stopdocumentation
%D \bgroup
%D
%D \setupframedtexts
%D   [setuptext]
%D   [background=color,backgroundcolor=white]
%D
%D \startbuffer
%D \setupbackground
%D   [backgroundoffset=4pt,
%D    background=screen,
%D    frame=on,
%D    framecolor=red,
%D    leftoffset=2pt]
%D \stopbuffer
%D
%D \getbuffer
%D
%D \startbackground
%D
%D \macros
%D   {setupbackground,startbackground,background}
%D
%D The section deals with backgrounds in the running text. This
%D means that texts is to be collected and split over pages. To
%D show what can be done, we provide this part of the
%D documentation with some gray background and a red frame.
%D Both the background and frame can have all characteristics
%D of \type{\framed}. This time we used the setting:
%D
%D \typebuffer
%D
%D The implementation is not that sophisticated, but suffices.
%D The main problem with this kind of functionality is to get
%D the spacing all right.

%D Specifying the background is more or less the same as
%D specifying a framed box.
%D
%D \showsetup{setupbackground}

\presetlocalframed[\??ag]

\def\dosetupbackground[#1]%
  {\getparameters[\??ag][#1]%
   \doifelse\@@agstate\v!start
     {\let\startbackground\dostartbackground
      \let\stopbackground \dostopbackground
      \let\background     \dobackground}
     {\let\startbackground\relax
      \let\stopbackground \relax
      \let\background     \relax}}

\def\setupbackground
  {\dosingleargument\dosetupbackground}

%D Actually typesetting the background is implemented rather
%D straightforward. We need to handle some spacing as well as
%D the (often) a bit smaller horizontal size.
%D
%D \showsetup{startbackground}
%D
%D Although we could have used a scratch one, we first
%D declare a boolean.

% 0=no-split, 1=no-split+indent, 2=split, 3=split+indent

\chardef\backgroundsplitmode\plusthree

%D The \type{\vbox to \lineheight{}\vskip\zeropoint}
%D construction gives the first real line a decent height by
%D adding a dummy line.

\def\dostartbackground
  {\endgraf
   \bgroup
   \setbox0\vbox\bgroup
     \vbox to \lineheight{}\vskip\zeropoint
     \blank[\v!disable]
     % \advance\hsize -\@@agleftoffset
     % \advance\hsize -\@@agrightoffset
     \leftskip \@@agleftoffset   % new **
     \rightskip\@@agrightoffset} % new **

%D This dummy line is removed by \type{\setbox2=\vsplit0 to
%D \lineheight}. That way \type{\topskip} takes care of the
%D lineheight. I'll probably forget to apply this trick
%D elsewhere.

\def\dostopbackground % improved version (i hope)
  {\endgraf
   \removelastskip
   \egroup
   \dimen2\leftskip % new **
   \forgetall
   \ifinsidefloat
     \chardef\backgroundsplitmode\zerocount
   \fi
   \ifcase\backgroundsplitmode
     \localframed[\??ag][\c!offset=\v!overlay]{\box0}%
   \or
     \hskip\dimen2
     \localframed[\??ag][\c!offset=\v!overlay]{\box0}%
   \else
     \splitmaxdepth\boxmaxdepth
     \splittopskip\topskip
     \setbox2\vsplit0 to \lineheight % get rid of fake line
     \loop
       \ifdim\pagetotal=\zeropoint % empty page
         \scratchdimen\textheight
         \chardef\backgroundsplit\plusone % split to max height
       \else
         \setbox\scratchbox\vbox{\@@agbefore}%
         \scratchdimen\dimexpr\pagegoal-\ht\scratchbox-\pagetotal\relax
         \chardef\backgroundsplit\plustwo % split to partial height
       \fi
       \advance\scratchdimen\dimexpr-\@@agtopoffset-\@@agbottomoffset\relax
       \ifdim\scratchdimen>2\lineheight\relax % reasonable, will be configurable
         \ifdim\ht0>\scratchdimen % larger than page
           \setbox2\vsplit0 to \scratchdimen
         \else
           \setbox2\box0
           \chardef\backgroundsplit\zerocount % no split
         \fi
         \setbox2\vbox \ifcase\backgroundsplit\or to \textheight \fi % max split
           {\vskip\@@agtopoffset
            \popsplitproperties
            \unvcopy2
            \prevdepth\dp2
            \obeydepth
            \vskip\@@agbottomoffset
            \vfill}
         \@@agbefore
         \ifcase\backgroundsplit\or\or % partial split
           \ifdim\pagegoal<\maxdimen
             \pagegoal=1.2\pagegoal % be a bit more tolerant
           \fi
         \fi
         \startlinecorrection
          %\localframed[\??ag][\c!offset=\v!overlay]{\hskip\@@agleftoffset\box2\hskip\@@agrightoffset}%
           \ifnum\backgroundsplitmode=\plusthree \hskip\dimen2 \fi %
           \localframed[\??ag][\c!offset=\v!overlay]{\box2}% new **
         \stoplinecorrection
         \ifcase\backgroundsplit % no split
           \@@agafter
         \else % some split
           \vfill\eject % geen \page !
         \fi
       \else
         \page
       \fi
     \ifdim\ht0>\zeropoint \repeat
   \fi
   \egroup
   \endgraf}

%D As a bonus we also have a short command, that is of not
%D much use, but kept there for historic reasons.
%D
%D \showsetup{background}

\def\dobackground
  {\bgroup
   \dowithnextbox
     {\localframed[\??ag][\c!offset=\v!overlay]{\flushnextbox}\egroup}
     \vbox}

%D \stopdocumentation
%D \stopbackground
%D \egroup

%D New, for the moment private; let's see when GB finds out
%D about this one and its obscure usage. It's used in:
%D
%D \startbuffer
%D \defineframedtext
%D   [tabulateframe]
%D   [offset=overlay,
%D    backgroundoffset=3pt,
%D    background=color,
%D    backgroundcolor=green]
%D
%D \setuptabulate
%D   [tabulate]
%D   [frame=tabulateframe]
%D
%D \setuptables
%D   [frame=tabulateframe]
%D
%D \input tufte
%D
%D \starttabulate[|l|l|]
%D \NC test \NC test \NC \NR \NC test \NC test \NC \NR
%D \NC test \NC test \NC \NR \NC test \NC test \NC \NR
%D \stoptabulate
%D
%D \input tufte
%D
%D \starttable[|l|l|]
%D \NC test \NC test \NC \AR \NC test \NC test \NC \AR
%D \NC test \NC test \NC \AR \NC test \NC test \NC \AR
%D \stoptable
%D \stopbuffer
%D
%D \typebuffer

\def\defineframedcontent
  {\dodoubleempty\dodefineframedcontent}

\def\dodefineframedcontent[#1][#2]%
  {\presetlocalframed[\??fc#1]%
   \getparameters[\??fc#1]
     [\c!leftoffset=\zeropoint,
      \c!rightoffset=\getvalue{\??fc#1\c!leftoffset},
      \c!topoffset=\zeropoint,
      \c!bottomoffset=\getvalue{\??fc#1\c!topoffset},
      \c!strut=\v!no,
      \c!offset=\v!overlay,
      \c!linecorrection=\v!no,
      \c!left=,
      \c!right=,
      #2]}

\let\setuplocalframed\getparameters

\def\setupframedcontent
  {\dodoubleempty\dosetupframedcontent}

\def\dosetupframedcontent[#1][#2]%
  {\def\docommand##1{\getparameters[\??fc##1][#2]}%
   \processcommacommand[#1]\docommand}

\def\startframedcontent[#1]%
  {\bgroup
   \let\stopframedcontent\egroup
   \doifnot{#1}\v!off
     {\doifdefined{\??fc#1\c!frame}
        {\def\stopframedcontent{\dostopframedcontent{#1}}%
         \dostartframedcontent{#1}}}}

\def\dostartframedcontent#1%
  {\setbox\framebox\hbox\bgroup
     \setlocalhsize
     \hsize\localhsize
     \advance\hsize\dimexpr-\getvalue{\??fc#1\c!leftoffset}-\getvalue{\??fc#1\c!rightoffset} \relax
     \advance\vsize\dimexpr-\getvalue{\??fc#1\c!topoffset} -\getvalue{\??fc#1\c!bottomoffset}\relax
     \hskip\getvalue{\??fc#1\c!leftoffset}%
     \vbox\bgroup
     \vskip\getvalue{\??fc#1\c!topoffset}%
     \vbox\bgroup
     \forgetall
     \blank[\v!disable]}

\def\dostopframedcontent#1%
  {\removelastskip
   \egroup
   \vskip\getvalue{\??fc#1\c!bottomoffset}%
   \egroup
   \hskip\getvalue{\??fc#1\c!rightoffset}%
   \egroup
   \doifvalue{\??fc#1\c!width}\v!fit
     {\letvalue{\??fc#1\c!width}\v!fixed}% no shapebox
   \ifinsidefloat
     \donefalse
   \else
     \doifelsevalue{\??fc#1\c!linecorrection}\v!yes\donetrue\donefalse
   \fi
   % plaats ?
   \ifdone\startlinecorrection\fi
   \getvalue{\??fc#1\c!left}% new
   \localframed[\??fc#1]{\box\framebox}%
   \getvalue{\??fc#1\c!right}% new
   \ifdone\stoplinecorrection\fi
   \egroup}

%D \macros
%D   {backgroundline}
%D
%D For the moment an undocumented feature, but a cancidate
%D for going public.

\def\backgroundline[#1]%
 %{\doifsomething{#1}{\dobackgroundline{#1}}\hbox}
  {\doifcolorelse{#1}{\dobackgroundline{#1}\hbox}\hbox}

\def\dobackgroundline#1%
  {\dowithnextbox
     {\hbox
        {\localcolortrue
         \startcolor[#1]%
         \vrule
           \!!width \nextboxwd
           \!!height\nextboxht
           \!!depth \nextboxdp
       \stopcolor
       \hskip-\nextboxwd
       \flushnextbox}}}

%D \macros
%D   {encircled}
%D
%D Some not so robust left||overs (borrowed from Knuth,
%D \TEX Book\ page 356):

\def\encircled#1%
  {{\ooalign{\hfil\raise0.07ex\hbox{{\tx#1}}\hfil\crcr\mathhexbox20D}}}

\let\omcirkeld\encircled

\setuplinewidth
  [\v!medium]

\setupframed
  [\c!width=\v!fit,
   \c!height=\v!broad,
   \c!lines=,
   \c!offset=0.25ex,  % \defaultframeoffset
   \c!empty=\v!no,
   \c!frame=\v!on,
   \c!topframe=,
   \c!bottomframe=,
   \c!leftframe=,
   \c!rightframe=,
   \c!radius=.5\bodyfontsize,
   \c!rulethickness=\linewidth,
   \c!corner=\v!rectangular,
   \c!depth=\!!zeropoint,
   \c!foregroundcolor=,
   \c!foregroundstyle=,
   \c!background=,
   \c!backgroundscreen=\@@rsscreen,
   \c!backgroundcolor=,
   \c!backgroundoffset=\!!zeropoint,
   \c!framecolor=,
   \c!frameoffset=\!!zeropoint,
   \c!component=,
   \c!align=,
   \c!bottom=\vss,
   \c!top=,
   \c!strut=\v!yes,
   \c!autostrut=\v!yes,
   \c!location=\v!normal,
   \c!orientation=,
   \c!autowidth=\v!yes,
   \c!setups=]

\setupscreens
  [%\c!factor=1.0,         % obsolete
   %\c!method=\v!external, % obsolete
   \c!screen=0.95]

\setupblackrules
  [\c!n=3,
   \c!width=1em,
   \c!height=1ex,
   \c!depth=\!!zeropoint,
   \c!alternative=\c!a,
   \c!distance=.25ex,
   \c!color=]

\setupmarginrules
  [\c!level=0,
   \c!rulethickness=\@@kadefaultwidth\linewidth]

\setupthinrules
  [\c!interlinespace=\v!small,
   \c!n=3,
   \c!before=,
   \c!inbetween={\blank[\v!white]},
   \c!after=,
   \c!color=,
   \c!height=.5\linewidth,
   \c!depth=.5\linewidth,
   \c!frame=\v!on, % compatible with textbackgrounds
   \c!alternative=\v!b,
   \c!backgroundcolor=,
   \c!background=,
   \c!rulethickness=]

\setuptextrules
  [\c!location=\v!left,
   \c!before=\blank,
   \c!after=\blank,
   \c!inbetween=,
   \c!width=2em,
   \c!style=\v!bold,
   \c!color=,
   \c!rulecolor=,
   \c!bodyfont=,
   \c!distance=.5em]

\setupfillinrules
  [\c!width=\v!broad,
   \c!distance=1em,
   \c!before=\blank,
   \c!after=\blank,
   \c!n=1,
   \c!interlinespace=\v!small,
   \c!separator=,
   \c!style=\v!normal,
   \c!color=]

\setupfillinlines
  [\c!width=3cm,
   \c!margin=\@@ivwidth,
   \c!distance=1em,
   \c!before=\blank,
   \c!after=\blank]

\setupbackground
  [\c!leftoffset=.5\bodyfontsize,
   \c!rightoffset=\@@agleftoffset,
   \c!topoffset=\!!zeropoint,
   \c!bottomoffset=\@@agtopoffset,
   \c!state=\v!start,
   \c!radius=.5\bodyfontsize,
   \c!corner=\v!rectangular,
   \c!frame=\v!off,
   \c!color=,
   \c!depth=\!!zeropoint,
   \c!background=\v!screen,
   \c!backgroundcolor=\@@agcolor,
   \c!screen=\@@rsscreen,
   \c!before=,
   \c!after=]

\protect \endinput
