%D \module
%D   [       file=pack-rul, % was core-rul,
%D        version=1998.10.16,
%D          title=\CONTEXT\ Packaging Macros,
%D       subtitle=Ruled Content,
%D         author=Hans Hagen,
%D           date=\currentdate,
%D      copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
%C
%C This module is part of the \CONTEXT\ macro||package and is
%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
%C details.

\writestatus{loading}{ConTeXt Packaging Macros / Ruled Content}

%D The code here is expanded lots of time as framed is used in many places. This is
%D why the code here is (and gets) optimized as much as possible. Also, by avoiding
%D packaging and expansion we also keep tracing reasonable. For instance, multiple
%D stacked backgrounds can slow down a run if not optimized this way.

\newinteger  \framednoflines
\newdimension\framedfirstheight
\newdimension\framedlastdepth
\newdimension\framedminwidth
\newdimension\framedmaxwidth
\newdimension\framedaveragewidth

\registerctxluafile{pack-rul}{autosuffix}

\unprotect

% \definesystemvariable {ol}   % OmLijnd -> check scrn-fld too

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

\newdimension\linewidth

\permanent\tolerant\protected\def\setuplinewidth[#1]%
  {\assigndimension{#1}\linewidth{.2\points}{.4\points}{.6\points}}

%D The parameter handler:

\installcorenamespace{framed}
\installcorenamespace{framedtop}
\installcorenamespace{framedbottom}
\installcorenamespace{framedleft}
\installcorenamespace{framedright}

\installcorenamespace{regularframed}
\installcorenamespace{simplifiedframed}

\installcommandhandler \??framed {framed} \??framed

\aliased\let\pack_framed_framedparameter    \framedparameter
\aliased\let\pack_framed_framedparameterhash\framedparameterhash
\aliased\let\pack_framed_setupcurrentframed \setupcurrentframed

\def\pack_framed_initialize
  {\enforced\let\framedparameter    \pack_framed_framedparameter
   \enforced\let\framedparameterhash\pack_framed_framedparameterhash
   \enforced\let\setupcurrentframed \pack_framed_setupcurrentframed
   \inframedtrue}

%D A helper:

\permanent\def\frameddimension#1{\the\dimexpr\framedparameter{#1}\relax}

%D Inheritance:

\permanent\protected\def\installinheritedframed#1%
  {\normalexpanded{\mult_interfaces_install_inherited_framed
     \expandafter\noexpand\csname current#1\endcsname
     \expandafter\noexpand\csname #1parameter\endcsname
     \expandafter\noexpand\csname #1parameterhash\endcsname
     \expandafter\noexpand\csname do#1parameter\endcsname
     \expandafter\noexpand\csname do#1parentparameter\endcsname
     \expandafter\noexpand\csname do#1rootparameter\endcsname
     \expandafter\noexpand\csname setupcurrent#1\endcsname
     \expandafter\noexpand\csname inherited#1framed\endcsname
     \expandafter\noexpand\csname inherited#1framedbox\endcsname}} % new

\protected\def\mult_interfaces_install_inherited_framed#1#2#3#4#5#6#7#8#9%
  {\enforced\frozen\def#5##1##2{\ifrelax##1#6{##2}\else#4{##1}{##2}\fi}%
   \enforced\frozen\def#6##1{\ifcsname\??framed:##1\endcsname\??framed:##1\else\??empty\fi}% root
   \frozen\instance\protected\def#8%
     {\bgroup
      \bgroup
      \inframedtrue
      \enforced\let\currentframed      #1%
      \enforced\let\framedparameter    #2%
      \enforced\let\framedparameterhash#3%
      \enforced\let\setupcurrentframed #7%
      \pack_framed_process_indeed}%
   \frozen\instance\protected\def#9%
     {\bgroup
      \inframedtrue
      \enforced\let\currentframed      #1%
      \enforced\let\framedparameter    #2%
      \enforced\let\framedparameterhash#3%
      \enforced\let\setupcurrentframed #7%
      \pack_framed_process_box_indeed}}

\permanent\protected\def\installframedcommandhandler#1#2#3%
  {\installcommandhandler{#1}{#2}{#3}%
   \installinheritedframed{#2}}

\permanent\protected\def\installframedautocommandhandler#1#2#3%
  {\installautocommandhandler{#1}{#2}{#3}%
   \installinheritedframed{#2}}

\permanent\protected\def\installsimpleframedcommandhandler#1#2#3%
  {\installsimplecommandhandler{#1}{#2}{#3}%
   \installinheritedframed{#2}}

% corner options (with radius=0pt rectangular):
%
%  0 round ltrb trbl rblt bltr
%  1 2 3 4 5 6 7 8 ltrb
%  9 lbr rbl / 10 tlb blt / 11 ltr rtl / 12 lbr rbl
% 13 rt tr / 14 rb br / 15 bl lb / 16 tl lt
% 32 lr rl / 33 tb bt
% 28 l / 29 r / 30 b / 31 t

%D \starttyping
%D \dostepwiserecurse{0}{33}{1}{
%D     \startTEXpage[offset=1dk]
%D         \dontleavehmode\ruledhbox{\framed
%D           [framecolor=red,
%D            framecorner=#1,
%D            frameradius=\zeropoint]
%D           {TEST #1}}
%D         \ruledhbox{\framed
%D           [framecolor=red,
%D            framecorner=#1,
%D            frameradius=10pt]
%D           {TEST #1}}
%D     \stopTEXpage
%D }
%D \stoptyping

% for regular framed

\setupframed
  [\c!width=\v!fit,
   \c!height=\v!broad,
  %\c!minheight=\zeropoint,
  %\c!lines=,
   \c!offset=.25\exheight,  % \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!backgroundcolor=,
   \c!backgroundoffset=\zeropoint,
  %\c!framecolor=,
   \c!frameoffset=\zeropoint,
   \c!backgroundcorner=\framedparameter\c!corner, % use \p_ here
   \c!backgroundradius=\framedparameter\c!radius,
   \c!backgrounddepth=\framedparameter\c!depth,
   \c!framecorner=\framedparameter\c!corner,
   \c!frameradius=\framedparameter\c!radius,
   \c!framedepth=\framedparameter\c!depth,
  %\c!component=,
  %\c!region=,
  %\c!align=,
   \c!bottom=\vss,
  %\c!top=,
   \c!strut=\v!yes,
   \c!autostrut=\v!yes,
   \c!location=\v!normal,
  %\c!orientation=,
  %\c!anchoring=,
   \c!autowidth=\v!yes,
  %\c!setups=,
   \c!synchronize=,
   \c!loffset=\zeropoint,
   \c!roffset=\zeropoint,
   \c!toffset=\zeropoint,
   \c!boffset=\zeropoint]

%D For backgrounds and such:

\defineframed
  [\??simplifiedframed]
  [\c!frame=\v!off,
   \c!depth=\zeropoint,
   \c!offset=\v!overlay,
   \c!component=,
   \c!region=,
   \c!radius=.5\bodyfontsize,
   \c!rulethickness=\linewidth,
   \c!corner=\v!rectangular,
   \c!backgroundoffset=\zeropoint,
   \c!frameoffset=\zeropoint,
   \c!backgroundcorner=\framedparameter\c!corner,  % use \p_ here
   \c!backgroundradius=\framedparameter\c!radius,
   \c!backgrounddepth=\framedparameter\c!depth,
   \c!framecorner=\framedparameter\c!corner,
   \c!frameradius=\framedparameter\c!radius,
   \c!framedepth=\framedparameter\c!depth,
   \c!location=\v!normal,
   \c!loffset=\zeropoint,
   \c!roffset=\zeropoint,
   \c!toffset=\zeropoint,
   \c!boffset=\zeropoint]

\permanent\protected\def\definesimplifiedframed[#1]% no settings
  {\defineframed[#1][\??simplifiedframed]%
   \enforced\letcsname#1\endcsname\undefined}

\letcsname\??simplifiedframed\endcsname\undefined

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

\newdimension\d_framed_target_wd
\newdimension\d_framed_target_ht
\newdimension\d_framed_target_dp
%newdimen    \d_framed_linewidth  \aliased\let   \ruledlinewidth\d_framed_linewidth % needed at lua end
\newdimension\d_framed_linewidth  \aliasdimension\ruledlinewidth\d_framed_linewidth % needed at lua end

\lettonothing\p_frame
\lettonothing\p_framed_adaptive
\lettonothing\p_framed_anchoring
\lettonothing\p_framed_autostrut
\lettonothing\p_framed_autowidth
\lettonothing\p_framed_background
\lettonothing\p_framed_backgroundcolor
\lettonothing\p_framed_backgroundcorner
\lettonothing\p_framed_backgroundoffset
\lettonothing\p_framed_backgroundradius
\lettonothing\p_framed_component
\lettonothing\p_framed_empty
\lettonothing\p_framed_foregroundcolor
\lettonothing\p_framed_foregroundstyle
\lettonothing\p_framed_frame            % \framedparameter\c!frame
\lettonothing\p_framed_framecolor
\lettonothing\p_framed_framecorner
\lettonothing\p_framed_frameradius
\lettonothing\p_framed_franalyze
\lettonothing\p_framed_lines
\lettonothing\p_framed_location
\lettonothing\p_framed_minheight
\lettonothing\p_framed_orientation
\lettonothing\p_framed_rulethickness
\lettonothing\p_framed_setups
\lettonothing\p_framed_synchronize
\lettonothing\p_framed_text_depthcorrection
\lettonothing\p_framed_text_strut

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

\def\pack_framed_filled_box
  {\edef\p_framed_backgroundcorner{\framedparameter\c!backgroundcorner}%
   \ifx\p_framed_backgroundcorner\v!rectangular
     \pack_framed_filled_box_normal
   \else
     \pack_framed_filled_box_radius
   \fi}

\def\pack_framed_filled_box_normal
  {\vrule
     \s!width \d_framed_target_wd
     \s!height\d_framed_target_ht
     \s!depth \d_framed_target_dp
   \relax}

\def\pack_framed_filled_box_radius
  {\edef\p_framed_backgroundradius{\framedparameter\c!backgroundradius}%
   \ifzeropt\dimexpr\p_framed_backgroundradius\relax % just in case of .x\bodyfontsize
     \pack_framed_filled_box_normal
   \else
     \pack_framed_filled_box_round
   \fi}

\def\pack_framed_filled_box_round
  {\raise\d_framed_target_dp\hpack{\frule
     type   fill
     width  \d_framed_target_wd
     height \d_framed_target_ht
     depth  \d_framed_target_dp
     line   \d_framed_linewidth
     radius \p_framed_backgroundradius\space
     corner {\p_framed_backgroundcorner}
   \relax}}

% we keep this as reference (should still work when uncommented)
%
% \def\pack_framed_stroked_box
%   {\edef\p_framed_framecorner{\framedparameter\c!framecorner}%
%    \ifx\p_framed_framecorner\v!rectangular
%      \pack_framed_stroked_box_normal
%    \else
%      \pack_framed_stroked_box_radius
%    \fi}
%
% \def\pack_framed_stroked_box_radius
%   {\edef\p_framed_frameradius{\framedparameter\c!frameradius}%
%    \ifzeropt\dimexpr\p_framed_frameradius\relax % just in case of .x\bodyfontsize
%      \pack_framed_stroked_box_normal
%    \orelse\ifx\p_framed_frame\v!on
%      \pack_framed_stroked_box_round
%    \fi}
%
% % \pack_framed_stroked_box_normal % later
%
% \def\pack_framed_stroked_box_round
%   {\raise\d_framed_target_dp\hpack{\frule
%      width  \d_framed_target_wd
%      height \d_framed_target_ht
%      depth  \d_framed_target_dp
%      line   \d_framed_linewidth
%      radius \p_framed_frameradius\space
%      corner {\p_framed_framecorner}
%    \relax}}
%
% corner is the parent of framecorner and backgroundcorner (round value never checked)
% when 'round' is passed it is not a number and therefore we get four sides

\def\pack_framed_stroked_box
  {\edef\p_framed_framecorner{\framedparameter\c!framecorner}%
   \ifx\p_framed_framecorner\v!rectangular
     \pack_framed_stroked_box_normal
   \orelse\ifx\p_framed_frame\v!on
     \pack_framed_stroked_box_round
   \fi}

\def\pack_framed_stroked_box_round % todo: variant without keywords
  {\raise\d_framed_target_dp
   \hpack{\clf_roundedoutline % we could access these at the lua end!
     \d_framed_target_wd
     \d_framed_target_ht
     \d_framed_target_dp
     \d_framed_linewidth
     \framedparameter\c!frameradius
     {\p_framed_framecorner}
   \relax}}

% 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 It won't be a surprise that we not only provide gray boxes, but also colored
%D ones. Here it is:

\def\pack_framed_background_box_color
  {\edef\p_framed_backgroundcolor{\framedparameter\c!backgroundcolor}%
   \ifempty\p_framed_backgroundcolor \else
     \doifcolor\p_framed_backgroundcolor\pack_framed_background_box_color_indeed
   \fi}

\def\pack_framed_background_box_color_indeed
  {\hpack{\dousecolorparameter\p_framed_backgroundcolor\pack_framed_filled_box}}

%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 backgrounds, we
%D introduce overlays. An overlay is something that contrary to its name lays {\em
%D under} the text. An 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 non||used arguments and
%D the two step call that enable the setting of the width, height and depth
%D variables. 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 the dimensions have a
%D 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.

\newdimension\d_overlay_width
\newdimension\d_overlay_height
\newdimension\d_overlay_depth
\newdimension\d_overlay_offset
\newdimension\d_overlay_linewidth

\lettonothing\m_overlay_region

% expandable ... in a future version the space will go (in mp one can use Overlay*)

\mutable  \def\overlaywidth        {\the\d_overlay_width    \space} % We preset the variables
\mutable  \def\overlayheight       {\the\d_overlay_height   \space} % to some reasonable default
\mutable  \def\overlaydepth        {\the\d_overlay_depth    \space} % values.
\mutable  \def\overlayoffset       {\the\d_overlay_offset   \space} % of the frame can be (are)
\mutable  \def\overlaylinewidth    {\the\d_overlay_linewidth\space} % set somewhere else.
\mutable  \def\overlayregion       {\m_overlay_region}

\mutable  \def\overlayradius       {\framedparameter\c!frameradius}
\mutable  \def\overlaycolor        {\framedparameter\c!backgroundcolor}
\mutable  \def\overlaylinecolor    {\framedparameter\c!framecolor}
%mutable  \edef\overlaycorner      {\framedparameter\c!backgroundcorner}
%mutable  \edef\overlayradius      {\framedparameter\c!backgroundradius}

\aliased\let\usedoverlaywidth    \d_overlay_width
\aliased\let\usedoverlayheight   \d_overlay_height
\aliased\let\usedoverlaydepth    \d_overlay_depth
\aliased\let\usedoverlayoffset   \d_overlay_offset
\aliased\let\usedoverlaylinewidth\d_overlay_linewidth

%D The next register is used to initialize overlays.

\newtoks\everyoverlay

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

\installcorenamespace{overlay}
\installcorenamespace{overlaybuiltin}

\appendtoks
    \oninterlineskip
\to \everyoverlay

\prependtoks
    \hsize\d_overlay_width
    \vsize\d_overlay_height
\to \everyoverlay

\protected\def\pack_overlay_define#1#2%
  {\defcsname\??overlay#1\endcsname{\executedefinedoverlay{#1}{#2}}}

\permanent\tolerant\protected\def\defineoverlay[#1]#*[#S#2]% wil be overloaded
% {\def\pack_framed_define_overlay_indeed##1{\defcsname\??overlay##1\endcsname{\executedefinedoverlay{##1}{#2}}}%
  {\def\pack_framed_define_overlay_indeed##1{\pack_overlay_define{##1}{#2}}%
   \processcommalist[#1]\pack_framed_define_overlay_indeed}

\permanent\protected\def\executedefinedoverlay#1#2% we can share the definitions
  {\bgroup % redundant grouping
   \setlayoutcomponentattribute{\v!overlay:#1}%
   \setbox\scratchbox\hbox \layoutcomponentboxattribute{\expand\everyoverlay#2}%
   \boxxoffset\scratchbox-.5\dimexpr\wd\scratchbox-\d_framed_target_wd\relax % was \d_overlay_width
   \boxyoffset\scratchbox-.5\dimexpr\ht\scratchbox-\d_framed_target_ht+\d_framed_target_dp\relax % not \d_overlay_height !
   \wd\scratchbox\d_framed_target_wd
   \ht\scratchbox\d_framed_target_ht
   \dp\scratchbox\d_framed_target_dp
   \box\scratchbox
   \egroup}

%D \macros
%D   {overlayfakebox}

\permanent\protected\def\overlayfakebox
  {\hpack % redundant but needs testing
     {\novrule
        \s!width \d_overlay_width
        \s!height\d_overlay_height
        \s!depth \zeropoint}}

%D For testing we provide:

\permanent\def\doifelseoverlay#1% only tests external overlays
  {\ifcsname\??overlay#1\endcsname
     \expandafter\firstoftwoarguments
   \else
     \expandafter\secondoftwoarguments
   \fi}

\aliased\let\doifoverlayelse\doifelseoverlay

%D The content of the box will be (temporary) saved in a box. We also have an
%D extra box for backgrounds.

\newbox\b_framed_normal
\newbox\b_framed_extra

\newtoks\everybackgroundbox

\lettonothing\m_framed_background % we might need a public name

\def\pack_framed_process_background
  {\ifcsname\??overlaybuiltin\m_framed_background\endcsname
     \expandafter\pack_framed_process_background_indeed_internal\lastnamedcs
   \orelse\ifcsname\??overlay\m_framed_background\endcsname
     \expandafter\pack_framed_process_background_indeed_external\lastnamedcs
   \fi}

\def\pack_framed_process_background_indeed_internal#1% % : in name
  {\bgroup
   \setbox\b_framed_extra\hpack\bgroup
     \ifzeropt\framedbackgroundoffset\else
       \kern-\framedbackgroundoffset
     \fi
     \hbox\bgroup#1\egroup
   \egroup
   \wd\b_framed_extra\zeropoint
   \ht\b_framed_extra\framedbackgroundheight
   \dp\b_framed_extra\framedbackgrounddepth
   \box\b_framed_extra
   \egroup}

\let\pack_framed_overlay_initialize\relax

\def\pack_framed_process_background_indeed_external
  {\pack_framed_overlay_initialize
   \pack_framed_process_background_indeed_internal}

\def\pack_framed_process_backgrounds#1,#2% #2 gobbles spaces (we could avoid one catch if we have nextbackground)
  {\edef\m_framed_background{#1}%
   \ifx\m_framed_background\s!unknown\else
     \pack_framed_process_background
     \expandafter\pack_framed_process_backgrounds
   \fi#2}

%D Beware, a backgroundbox can be empty which is another reason why we set the
%D width to zero instead of back-skipping.

\newdimension\framedbackgroundwidth
\newdimension\framedbackgroundheight
\newdimension\framedbackgrounddepth
\newdimension\framedbackgroundoffset

\aliased\let\foregroundbox\relax

\def\pack_framed_background_box_content% fuzzy but needed hack, this \vss, otherwise
  {\vpack to \framedbackgroundheight{\vss\box\b_framed_normal\vss}} % vertical shift \backgroundheight

\def\pack_framed_set_region % experiment
  {\ifx\m_overlay_region\v!yes
     \edef\m_overlay_region{\reservedautoregiontag}%
   \fi}

\def\pack_framed_add_region % experiment, zerocount forces the given region
  {\anch_mark_tagged_box\b_framed_normal\m_overlay_region\zerocount}

\def\pack_framed_add_background
  {\ifconditional\c_pack_reanchor
      \analyzelocalanchors\b_framed_normal
   \fi
   \setbox\b_framed_normal\hpack % was vbox % see also *1*
     {%\pack_framed_forgetall % can be relaxed
      \boxmaxdepth\maxdimen
      \framedbackgroundoffset\d_framed_backgroundoffset
      \framedbackgroundwidth \wd\b_framed_normal
      \framedbackgroundheight\ht\b_framed_normal
      \framedbackgrounddepth \dp\b_framed_normal
      \d_framed_target_wd\dimexpr\framedbackgroundwidth +2\framedbackgroundoffset\relax
      \d_framed_target_ht\dimexpr\framedbackgroundheight+ \framedbackgroundoffset\relax
      \d_framed_target_dp\dimexpr\framedbackgrounddepth + \framedbackgroundoffset+\framedparameter\c!backgrounddepth\relax
      \let\pack_framed_overlay_initialize\pack_framed_overlay_initialize_indeed
      \ifempty\p_framed_component
        \resetlayoutcomponentattribute
      \else
        \setlayoutcomponentattribute{\v!background:\p_framed_component}%
      \fi
      \enforced\let\foregroundbox\pack_framed_background_box_content
      \hpack \layoutcomponentboxattribute to \framedbackgroundwidth\bgroup % width in case 'foreground' is used as overlay
         \expand\everybackgroundbox % moved
         \expandafter\pack_framed_process_backgrounds\p_framed_background,\s!unknown,\relax % hm, messy .. look into it
         \box\b_framed_normal
         \hss
      \egroup}}

\def\pack_framed_overlay_initialize_indeed
  {\d_overlay_width     \d_framed_target_wd
   \d_overlay_height    \dimexpr\d_framed_target_ht+\d_framed_target_dp\relax
   \d_overlay_depth     \d_framed_target_dp
   \d_overlay_linewidth \d_framed_linewidth
   \d_overlay_offset    \framedbackgroundoffset\relax
   % We expand these once:
   \edef\overlaycolor    {\framedparameter\c!backgroundcolor}%
   \edef\overlaylinecolor{\framedparameter\c!framecolor}% only needed for layers
   %
   \let\pack_framed_overlay_initialize\relax}

%D One can explictly insert the foreground box. For that purpose we introduce the
%D overlay \type {foreground}.
%D
%D We predefine two already familiar backgrounds:

%letcsname\??overlaybuiltin\v!screen    \endcsname\pack_framed_background_box_gray
\letcsname\??overlaybuiltin\v!color     \endcsname\pack_framed_background_box_color
\letcsname\??overlaybuiltin\v!foreground\endcsname\pack_framed_background_box_content % replaces: \defineoverlay[\v!foreground][\foregroundbox]

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

\def\pack_framed_add_outline
  {\setbox\b_framed_normal\hpack % rules on top of box
     {\d_framed_target_wd\dimexpr\wd\b_framed_normal+2\d_framed_frameoffset\relax
      \d_framed_target_ht\dimexpr\ht\b_framed_normal+ \d_framed_frameoffset\relax
      \d_framed_target_dp\dimexpr\dp\b_framed_normal+ \d_framed_frameoffset+\framedparameter\c!framedepth\relax
      \ifdim\d_framed_target_dp<\zeropoint
        \advanceby\d_framed_target_ht \d_framed_target_dp
        \scratchdimen-\d_framed_target_dp
        \d_framed_target_dp\zeropoint
      \else
        \scratchdimen\zeropoint
      \fi
      \edef\overlaylinecolor{\framedparameter\c!framecolor}% twice, also in background
      \setbox\b_framed_extra\hpack
        {\kern-\d_framed_frameoffset
         \raise\scratchdimen
         \hpack{\ifempty\overlaylinecolor\else\dousecolorparameter\overlaylinecolor\fi\pack_framed_stroked_box}}%
      \wd\b_framed_extra\wd\b_framed_normal
      \ht\b_framed_extra\ht\b_framed_normal
      \dp\b_framed_extra\dp\b_framed_normal
      \wd\b_framed_normal\zeropoint
      \box\b_framed_normal
      \box\b_framed_extra}}

%D A probably unknown feature:
%D
%D \startbuffer
%D \hpack\bgroup
%D     \framed[framecolor=MyColor,frame=on]    {!!!!!!!!}
%D     \framed[framecolor=MyColor,frame=closed]{!!!!!!!!}
%D     \framed[framecolor=MyColor,frame=small] {!!!!!!!!}
%D \egroup
%D \stopbuffer
%D
%D \typebuffer \startlinecorrection[blank] \getbuffer \stoplinecorrection

% \def\pack_framed_stroked_box_normal_opened
%   {\setbox\scratchbox\vpack \bgroup
%      \csname\??framedtop\p_framed_frame\framedparameter\c!topframe\endcsname
%      \nointerlineskip % new (needed for fences)
%      \hpack \bgroup
%        \csname\??framedleft\p_framed_frame\framedparameter\c!leftframe\endcsname
%        \novrule
%          \s!width \d_framed_target_wd
%          \s!height\d_framed_target_ht
%          \s!depth \d_framed_target_dp
%        \csname\??framedright\p_framed_frame\framedparameter\c!rightframe\endcsname
%      \egroup
%      \nointerlineskip % new (needed for fences)
%      \csname\??framedbottom\p_framed_frame\framedparameter\c!bottomframe\endcsname
%    \egroup
%    \wd\scratchbox\d_framed_target_wd
%    \ht\scratchbox\d_framed_target_ht
%    \dp\scratchbox\d_framed_target_dp
%    \box\scratchbox}

% less logging:

\def\pack_framed_stroked_box_normal_opened
  {\setbox\scratchbox\vpack \bgroup
   % \normaloffinterlineskip % sets baselineskip, lineskip and lineskiplimit
     \baselineskip-\thousandpoint
     \lineskip     \zeroskip
     \lineskiplimit\maxdimen
     \csname\??framedtop\p_framed_frame\framedparameter\c!topframe\endcsname
   % \nointerlineskip % new (needed for fences)
     \hpack \bgroup
       \csname\??framedleft\p_framed_frame\framedparameter\c!leftframe\endcsname
       \novrule
         \s!width \d_framed_target_wd
         \s!height\d_framed_target_ht
         \s!depth \d_framed_target_dp
       \csname\??framedright\p_framed_frame\framedparameter\c!rightframe\endcsname
     \egroup
   % \nointerlineskip % new (needed for fences)
     \csname\??framedbottom\p_framed_frame\framedparameter\c!bottomframe\endcsname
   \egroup
   \wd\scratchbox\d_framed_target_wd
   \ht\scratchbox\d_framed_target_ht
   \dp\scratchbox\d_framed_target_dp
   \box\scratchbox}

\def\pack_framed_stroked_box_normal_closed
  {\hpack\bgroup
     \scratchdimen.5\d_framed_linewidth
     \kern\scratchdimen
     \clf_framedoutline
       \dimexpr\d_framed_target_wd-\d_framed_linewidth\relax
       \dimexpr\d_framed_target_ht-\scratchdimen\relax
       \dimexpr\d_framed_target_dp-\scratchdimen\relax
       \d_framed_linewidth
     \relax
   \egroup}

\def\pack_framed_stroked_box_normal
  {\ifx\p_framed_frame\v!closed
     \pack_framed_stroked_box_normal_closed
   \else
     \pack_framed_stroked_box_normal_opened
   \fi}

\def\pack_framed_t_rule{\hrule\s!height\d_framed_linewidth\kern-\d_framed_linewidth}
\def\pack_framed_b_rule{\kern-\d_framed_linewidth\hrule\s!height\d_framed_linewidth}
\def\pack_framed_r_rule{\kern-\d_framed_linewidth\vrule\s!width\d_framed_linewidth}
\def\pack_framed_l_rule{\vrule\s!width\d_framed_linewidth\kern-\d_framed_linewidth}

\letcsname\??framedtop   \v!on \v!on\endcsname\pack_framed_t_rule
\letcsname\??framedtop   \v!off\v!on\endcsname\pack_framed_t_rule
\letcsname\??framedtop   \v!on      \endcsname\pack_framed_t_rule

\letcsname\??framedbottom\v!on \v!on\endcsname\pack_framed_b_rule
\letcsname\??framedbottom\v!off\v!on\endcsname\pack_framed_b_rule
\letcsname\??framedbottom\v!on      \endcsname\pack_framed_b_rule

\letcsname\??framedleft  \v!on \v!on\endcsname\pack_framed_l_rule
\letcsname\??framedleft  \v!off\v!on\endcsname\pack_framed_l_rule
\letcsname\??framedleft  \v!on      \endcsname\pack_framed_l_rule

\letcsname\??framedright \v!on \v!on\endcsname\pack_framed_r_rule
\letcsname\??framedright \v!off\v!on\endcsname\pack_framed_r_rule
\letcsname\??framedright \v!on      \endcsname\pack_framed_r_rule

% no overlapping rules

% \def\pack_framed_t_rules{\hpack{\kern\d_framed_linewidth\vrule\s!width\dimexpr\d_framed_target_wd-2\d_framed_linewidth\relax\s!height\d_framed_linewidth}\nointerlineskip\kern-\d_framed_linewidth}
% \def\pack_framed_b_rules{\kern-\d_framed_linewidth\nointerlineskip\hpack{\kern\d_framed_linewidth\vrule\s!width\dimexpr\d_framed_target_wd-2\d_framed_linewidth\relax\s!height\d_framed_linewidth}}
% \def\pack_framed_r_rules{\kern-\d_framed_linewidth\vrule\s!height\dimexpr\d_framed_target_ht-\d_framed_linewidth\relax\s!depth-\d_framed_linewidth\s!width\d_framed_linewidth}
% \def\pack_framed_l_rules{\vrule\s!height\dimexpr\d_framed_target_ht-\d_framed_linewidth\relax\s!depth-\d_framed_linewidth\s!width\d_framed_linewidth\kern-\d_framed_linewidth}
%
% see above, less tracing
%
% \def\pack_framed_t_rules{\hpack{\kern\d_framed_linewidth\vrule\s!width\dimexpr\d_framed_target_wd-2\d_framed_linewidth\relax\s!height\d_framed_linewidth}\kern-\d_framed_linewidth}
% \def\pack_framed_b_rules{\kern-\d_framed_linewidth\hpack{\kern\d_framed_linewidth\vrule\s!width\dimexpr\d_framed_target_wd-2\d_framed_linewidth\relax\s!height\d_framed_linewidth}}
% \def\pack_framed_r_rules{\kern-\d_framed_linewidth\vrule\s!height\dimexpr\d_framed_target_ht-\d_framed_linewidth\relax\s!depth-\d_framed_linewidth\s!width\d_framed_linewidth}
% \def\pack_framed_l_rules{\vrule\s!height\dimexpr\d_framed_target_ht-\d_framed_linewidth\relax\s!depth-\d_framed_linewidth\s!width\d_framed_linewidth\kern-\d_framed_linewidth}
%
% more modern:

\def\pack_framed_rules_hrule{\hrule\s!height\d_framed_linewidth\s!left\d_framed_linewidth\s!right \d_framed_linewidth\relax}
\def\pack_framed_rules_vrule{\vrule\s!width \d_framed_linewidth\s!top \d_framed_linewidth\s!bottom\d_framed_linewidth\relax}% \s!yoffset-\d_framed_linewidth}

\def\pack_framed_t_rules{\pack_framed_rules_hrule\kern-\d_framed_linewidth}
\def\pack_framed_b_rules{\kern-\d_framed_linewidth\pack_framed_rules_hrule}
\def\pack_framed_r_rules{\kern-\d_framed_linewidth\pack_framed_rules_vrule}
\def\pack_framed_l_rules{\pack_framed_rules_vrule\kern-\d_framed_linewidth}

\letcsname\??framedtop   \v!small\v!small\endcsname\pack_framed_t_rules
\letcsname\??framedtop   \v!off  \v!small\endcsname\pack_framed_t_rules
\letcsname\??framedtop   \v!small        \endcsname\pack_framed_t_rules

\letcsname\??framedbottom\v!small\v!small\endcsname\pack_framed_b_rules
\letcsname\??framedbottom\v!off  \v!small\endcsname\pack_framed_b_rules
\letcsname\??framedbottom\v!small        \endcsname\pack_framed_b_rules

\letcsname\??framedleft  \v!small\v!small\endcsname\pack_framed_l_rules
\letcsname\??framedleft  \v!off  \v!small\endcsname\pack_framed_l_rules
\letcsname\??framedleft  \v!small        \endcsname\pack_framed_l_rules

\letcsname\??framedright \v!small\v!small\endcsname\pack_framed_r_rules
\letcsname\??framedright \v!off  \v!small\endcsname\pack_framed_r_rules
\letcsname\??framedright \v!small        \endcsname\pack_framed_r_rules

% \framed
%   [width=4cm,height=3cm,rulethickness=3mm,
%    frame=off,rightframe=on,leftframe=on,topframe=on,bottomframe=on]
%   {}
% \framed
%   [width=4cm,height=3cm,rulethickness=3mm,
%    frame=off,rightframe=small,leftframe=small,topframe=small,bottomframe=small]
%   {}
% \framed
%   [width=4cm,height=3cm,rulethickness=3mm,
%    frame=off,rightframe=small,leftframe=small,topframe=small,bottomframe=on]
%   {}

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

% todo   : \c_framed_hasoffset
% faster : \let\c_framed_hasoffset\falseconditional

\newconditional\c_framed_has_offset
\newconditional\c_framed_has_width
\newconditional\c_framed_has_height
\newconditional\c_framed_has_format
\newconditional\c_framed_is_overlaid
\newconditional\c_framed_has_frame
\newconditional\c_framed_has_extra_offset
\newconditional\c_framed_text_location_none

\newconstant   \c_framed_has_strut  % 0=relaxes 1=pseudostruts 2=realstruts

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

\mutable\def\defaultframeoffset{.25\exheight}

\installcorenamespace{regularframedlevel}

\permanent\protected\def\installregularframed#1%
  {\defineframed[#1]}

\permanent\protected\def\presetlocalframed[#1]%
  {\defineframed[#1]}

% \presetlocalframed[\??framed]

\newinteger\c_pack_framed_nesting

\permanent\tolerant\protected\def\framed[#S#1]%
  {\bgroup
   \advanceby\c_pack_framed_nesting\plusone
   \letcsname\??framed>\the\c_pack_framed_nesting:\s!parent\endcsname\??framed
   \cdef\currentframed{>\the\c_pack_framed_nesting}%
   \pack_framed_initialize
   \bgroup
   \setupcurrentframed[#1]% here !, seldom no argument so no need to optimize
   \pack_framed_process_indeed}

\permanent\tolerant\protected\def\startframed[#S#1]#*[#S#2]%
  {\bgroup
   \ifarguments
     \pack_framed_initialize
     \bgroup
   \or
     \ifhastok={#1}%
       \advanceby\c_pack_framed_nesting\plusone
       \letcsname\??framed>\the\c_pack_framed_nesting:\s!parent\endcsname\??framed
       \cdef\currentframed{>\the\c_pack_framed_nesting}%
       \pack_framed_initialize
       \bgroup
       \setupcurrentframed[#1]% here !
     \else
       \cdef\currentframed{#1}%
       \pack_framed_initialize
       \bgroup
     \fi
   \else
     \cdef\currentframed{#1}%
     \pack_framed_initialize
     \bgroup
     \setupcurrentframed[#2]% here !
   \fi
   \pack_framed_process_indeed
   \bgroup
   \ignorespaces}

% till here

\permanent\protected\def\stopframed
  {\removeunwantedspaces
   \egroup}

\permanent\protected\def\normalframedwithsettings[#S#1]%
  {\bgroup
   \advanceby\c_pack_framed_nesting\plusone
   \letcsname\??framed>\the\c_pack_framed_nesting:\s!parent\endcsname\??framed
   \bgroup
   \cdef\currentframed{>\the\c_pack_framed_nesting}%
   \pack_framed_initialize
   \setupcurrentframed[#1]%
   \pack_framed_process_indeed}

%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 \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 looking for the user
%D supplied ones. The first step is omitted in the local case, because these are
%D preset at declaration time and keep their values unless explictly changed. By
%D presetting the variables everytime the normal command is called, we can use this
%D command nested, without the unwanted side effect of inheritance. The boolean is
%D used to speed up the color stack.

\newif\ifinframed

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

%% \protected\def\fastlocalframed[#1]#2[#3]#4% 3-4

\permanent\protected\def\fastlocalframed[#1]#*[#S#2]#3%
  {\bgroup
   \cdef\currentframed{#1}%
   \pack_framed_initialize
   \setbox\b_framed_normal\hbox{#3}%
   \iftrialtypesetting \else
     \edef\m_overlay_region{\framedparameter\c!region}%
     \ifempty\m_overlay_region\else
       \pack_framed_set_region
     \fi
   \fi
   \setupcurrentframed[#2]%
   \edef\p_framed_rulethickness{\framedparameter\c!rulethickness}% also used in backgrounds
   \d_framed_frameoffset\framedparameter\c!frameoffset\relax     % also used in backgrounds
   \edef\p_framed_frame{\framedparameter\c!frame}%
   \edef\p_framed_background{\framedparameter\c!background}%
   % not here, in calling macro: setups
   \pack_framed_remove_depth
   % beware, depth goes away when we have a frame, otherwise it's retained
   \ifx\p_framed_frame\v!overlay \orelse \ifx\p_framed_frame\v!none \else
     \ifempty\p_framed_rulethickness\else
        \d_framed_linewidth\p_framed_rulethickness\relax
     \fi
     \pack_framed_add_outline % real or invisible frame
   \fi
   \ifempty\p_framed_background \else
     \edef\p_framed_backgroundoffset{\framedparameter\c!backgroundoffset}%
     \d_framed_backgroundoffset
       \ifx\p_framed_backgroundoffset\v!frame
         \d_framed_frameoffset
       \else
         \p_framed_backgroundoffset
       \fi
     \edef\p_framed_component{\framedparameter\c!component}%
     \pack_framed_add_background
   \fi
   \pack_framed_restore_depth
   \iftrialtypesetting\orelse\ifempty\m_overlay_region\else
     \pack_framed_add_region
   \fi
   \box\b_framed_normal
   \egroup}

%D The next macro uses a box and takes its natural width and height so these
%D can better be correct.

\protected\def\pack_framed_process_box_indeed#1#2% component box (assumes parameters set and grouped usage)
  {%
  %\setbox\b_framed_normal\box#2%
  %\def\b_framed_normal{#2}% protected against overload
   \enforced\let\b_framed_normal#2%
   %
   \edef\m_overlay_region{\framedparameter\c!region}%
   \ifempty\m_overlay_region\else
     \pack_framed_set_region
   \fi
   \edef\p_framed_rulethickness{\framedparameter\c!rulethickness}% also used in backgrounds
   \d_framed_frameoffset\framedparameter\c!frameoffset\relax     % also used in backgrounds
   \edef\p_framed_frame{\framedparameter\c!frame}%
   \edef\p_framed_background{\framedparameter\c!background}%
   \ifx\p_framed_frame\v!overlay \orelse \ifx\p_framed_frame\v!none \else
     \ifempty\p_framed_rulethickness \else
        \d_framed_linewidth\p_framed_rulethickness\relax
     \fi
     \pack_framed_add_outline % real or invisible frame
   \fi
   \ifempty\p_framed_background \else
     \edef\p_framed_backgroundoffset{\framedparameter\c!backgroundoffset}%
     \d_framed_backgroundoffset
       \ifx\p_framed_backgroundoffset\v!frame
         \d_framed_frameoffset
       \else
         \p_framed_backgroundoffset
       \fi
     \edef\p_framed_component{#1}%
     \pack_framed_add_background
   \fi
   \ifempty\m_overlay_region\else
     \pack_framed_add_region
   \fi
   \box\b_framed_normal
   \egroup}

\permanent\protected\def\localbackgroundframed#1% namespace component box
  {\bgroup
   \cdef\currentframed{#1}%
   \pack_framed_initialize
   \pack_framed_process_box_indeed} % group ends here

\mutable\let\postprocessframebox\relax

%D A nice example by Aditya:
%D
%D \starttyping
%D \setupframed
%D   [loffset=\framedparameter{hoffset},
%D    roffset=\framedparameter{hoffset},
%D    hoffset=\zeropoint]
%D
%D \defineframed[test][hoffset=1cm]
%D \stoptyping

%D A byproduct of \type {\uleaders} in the 2022 math upgrade project:
%D
%D \starttyping
%D \startsetups adaptive:test:a
%D     \setbox\usedadaptivebox\vbox to \usedadaptivetotal \bgroup
%D         \externalfigure
%D           [cow.pdf]
%D           [width=\framedmaxwidth,
%D            frame=on,
%D            height=\usedadaptivetotal]%
%D    \egroup
%D \stopsetups
%D
%D \startsetups adaptive:test:b
%D     \setbox\usedadaptivebox\vbox to \usedadaptivetotal \bgroup
%D         \externalfigure
%D           [cow.pdf]
%D           [width=\usedadaptivewidth,
%D            frame=on,
%D            height=\usedadaptivetotal]%
%D    \egroup
%D \stopsetups
%D
%D \framed[height=18cm,align=middle,adaptive=yes,top=,bottom=] {%
%D     \begstrut \samplefile{tufte} \endstrut
%D     \par
%D     \adaptivevbox
%D       [strut=yes,setups=adaptive:test:a]
%D       {\showstruts\strut\hsize5cm\hss}%
%D     \par
%D     \adaptivevbox
%D       [strut=yes,setups=adaptive:test:b]
%D       {\showstruts\strut\hsize5cm\hss}%
%D     \par
%D     \begstrut \samplefile{tufte} \endstrut
%D }
%D \stoptyping

\newdimension\d_framed_width
\newdimension\d_framed_height
\newdimension\d_framed_frameoffset
\newdimension\d_framed_backgroundoffset
\newdimension\d_framed_local_offset

% todo: protect local \framednames

\permanent\tolerant\protected\def\localframed[#1]#*[#S#2]%
  {\bgroup
   \bgroup
   \cdef\currentframed{#1}%
   \pack_framed_initialize
   \setupcurrentframed[#2]% here !
   \pack_framed_process_indeed}

\permanent\protected\def\directlocalframed[#1]% no optional
  {\bgroup
   \bgroup
   \cdef\currentframed{#1}%
   \pack_framed_initialize
   \pack_framed_process_indeed}

\aliased\let\localframedwithsettings\localframed

% done

\defineinterfaceconstant {fr!analyze} {fr:analyze} % private option

\permanent\protected\lettonothing\delayedbegstrut
\permanent\protected\lettonothing\delayedendstrut
\permanent\protected\lettonothing\delayedstrut

\permanent\protected\lettonothing\localbegstrut
\permanent\protected\lettonothing\localendstrut
\permanent\protected\lettonothing\localstrut

\mutable\lettonothing\localoffset
\mutable\lettonothing\localwidth
\mutable\lettonothing\localheight
\mutable\lettonothing\localformat

\mutable\let\framedwidth \zeropoint
\mutable\let\framedheight\zeropoint
\mutable\let\framedoffset\zeropoint

\pushoverloadmode
    \newuserunit\framedwidth  fw % checked \d_framed_width
    \newuserunit\framedheight fh % checked \d_framed_height
    \newuserunit\framedoffset fo % checked \d_framed_local_offset
    \newuserunit\linewidth    lw
\popoverloadmode

% better a constant:

\newconditional\c_pack_resync
\newconditional\c_pack_reanchor

% \let\spac_framed_pop_local_anchors\relax

\def\spac_framed_push_local_anchors
  {\ifx\p_framed_synchronize\v!yes
     \c_pack_resync\conditionaltrue
     \c_pack_reanchor\conditionaltrue
     \pushlocalanchors
   % \aftergrouped{\aftergrouped{\poplocalanchors}}%
     \aftergroup\poplocalanchors
   \orelse\ifx\p_framed_synchronize\v!text
     \c_pack_resync\conditionaltrue
     \pushlocalanchors
     \aftergroup\poplocalanchors
   \orelse\ifx\p_framed_synchronize\v!background
     \c_pack_reanchor\conditionaltrue
     \pushlocalanchors
     \aftergroup\poplocalanchors
   \else
     \c_pack_resync\conditionalfalse
     \c_pack_reanchor\conditionalfalse
   \fi}

\protected\def\pack_framed_process_indeed
  {\d_framed_frameoffset\framedparameter\c!frameoffset
   \edef\p_framed_backgroundoffset{\framedparameter\c!backgroundoffset}%
   \d_framed_backgroundoffset
     \ifx\p_framed_backgroundoffset\v!frame
       \d_framed_frameoffset
     \else
       \p_framed_backgroundoffset
     \fi
   % new, experimental dirty hook
   \framedparameter\c!extras
   % to get the right spacing
   \edef\p_framed_foregroundstyle{\framedparameter\c!foregroundstyle}%
   \ifempty\p_framed_foregroundstyle\else\dousestyleparameter\p_framed_foregroundstyle\fi
   % beware, both the frame and background offset can be overruled
   %
   \edef\p_framed_setups{\framedparameter\c!setups}%
   % 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\p_strut    {\framedparameter\c!strut}%
   % these are not
   \edef\p_framed_autostrut  {\framedparameter\c!autostrut}%
   \edef\p_framed_frame      {\framedparameter\c!frame}%
   \edef\p_framed_location   {\framedparameter\c!location}%
   \edef\p_framed_orientation{\framedparameter\c!orientation}%
   \edef\p_framed_anchoring  {\framedparameter\c!anchoring}%
   \edef\p_framed_synchronize{\framedparameter\c!synchronize}%
   %
   \edef\p_framed_autowidth  {\framedparameter\c!autowidth}%
   \edef\p_framed_franalyze  {\framedparameter\c!fr!analyze}% experimental option
   %
   \ifempty\p_framed_synchronize
     \c_pack_resync\conditionalfalse
     \c_pack_reanchor\conditionalfalse
%      \let\spac_framed_pop_local_anchors\relax
   \else
     \spac_framed_push_local_anchors
   \fi
   %
   \ifx\p_framed_frame\v!overlay   % no frame, no offset, no framewidth
     \c_framed_has_frame\conditionalfalse
     \let\localoffset\v!overlay
   \orelse\ifx\p_framed_frame\v!none % no frame, no framewidth
     \c_framed_has_frame\conditionalfalse
   \else
     \c_framed_has_frame\conditionaltrue
   \fi
   \ifconditional\c_framed_has_frame
     \edef\p_framed_rulethickness{\framedparameter\c!rulethickness}%
     \ifempty\p_framed_rulethickness\else
        \d_framed_linewidth\p_framed_rulethickness\relax
     \fi
   \else
     \d_framed_linewidth\zeropoint
   \fi
   % 2013/03/12: a change of order (sizes before align
   \ifx\localwidth\v!local
     \setlocalhsize
   \fi
   %
   \forgetall % should happen after \localwidth but before align
   %
   \ifempty\localformat
     \c_framed_has_format\conditionalfalse
   \else
     \c_framed_has_format\conditionaltrue
     \dosetraggedcommand\localformat % not that fast
   \fi
   %
   \ifcsname\??framedoffsetalternative\localoffset\endcsname
     \lastnamedcs
   \else
     \framed_offset_alternative_unknown
   \fi
   \ifcsname\??framedwidthalternative\localwidth\endcsname
     \lastnamedcs
   \else
     \framed_width_alternative_unknown
   \fi
   \ifcsname\??framedheightalternative\localheight\endcsname
     \lastnamedcs
   \else
     \framed_height_alternative_unknown
   \fi
   % the next check could move to heightalternative
   \ifconditional\c_framed_has_height
     \ifcstok{\framedparameter\c!adaptive}\v!yes
       \let\p_framed_adaptive\s!delay
     \else
       \lettonothing\p_framed_adaptive
     \fi
   \else
     \lettonothing\p_framed_adaptive
     \edef\p_framed_lines{\framedparameter\c!lines}%
     \ifempty\p_framed_lines
     \orelse\ifcase\p_framed_lines
     \else
       \d_framed_height\p_framed_lines\lineheight
       \edef\localheight{\the\d_framed_height}%
       \c_framed_has_height\conditionaltrue
     \fi
   \fi
   % this is now an option: width=local
   %
   % \ifdim\d_framed_width=\hsize
   %   \parindent\zeropoint
   %   \setlocalhsize
   %   \d_framed_width\localhsize
   % \fi
   % i.e. disable (colsetbackgroundproblemintechniek)
   \advanceby\d_framed_width  -2\d_framed_local_offset
   \advanceby\d_framed_height -2\d_framed_local_offset
   \ifcsname\??framedstrutalternative\p_strut\endcsname
     \lastnamedcs
   \else
     \framed_offset_alternative_unknown
   \fi
   % the next check could move to strutalternative
   \ifcase\c_framed_has_strut % none (not even noindent)
     \enforced\lettonothing\localbegstrut
     \enforced\lettonothing\localendstrut
     \enforced\lettonothing\localstrut
   \or % no / overlay
     \enforced\let\localbegstrut\pseudobegstrut
     \enforced\let\localendstrut\pseudoendstrut
     \enforced\let\localstrut   \pseudostrut
   \else
     \enforced\let\localbegstrut\begstrut
     \enforced\let\localendstrut\endstrut
     \enforced\let\localstrut   \strut
   \fi
   \ifx\p_framed_autostrut\v!yes
     \enforced\lettonothing\delayedbegstrut
     \enforced\lettonothing\delayedendstrut
     \enforced\lettonothing\delayedstrut
   \else
     \enforced\let         \delayedbegstrut\localbegstrut
     \enforced\let         \delayedendstrut\localendstrut
     \enforced\let         \delayedstrut   \localstrut
     \enforced\lettonothing\localbegstrut
     \enforced\lettonothing\localendstrut
     \enforced\lettonothing\localstrut
   \fi
   \ifconditional\c_framed_has_height
     \enforced\let\\\pack_framed_vboxed_newline
     \ifconditional\c_framed_has_width
       \enforced\let\hairline\pack_framed_vboxed_hairline
       \ifconditional\c_framed_has_format
         \let\next\pack_framed_format_format_yes
       \else
         \let\next\pack_framed_format_format_nop
       \fi
     \else
       \enforced\let\hairline\pack_framed_hboxed_hairline
       \ifconditional\c_framed_has_format
         \let\next\pack_framed_format_format_height
       \else
         \let\next\pack_framed_format_format_vsize
       \fi
     \fi
   \orelse\ifconditional\c_framed_has_width
     \ifconditional\c_framed_has_format
       \enforced\let\\\pack_framed_vboxed_newline
       \enforced\let\hairline\pack_framed_vboxed_hairline
       \let\next\pack_framed_format_format_width
     \else
       \enforced\let\\\pack_framed_hboxed_newline
       \enforced\let\hairline\pack_framed_hboxed_hairline
       \let\next\pack_framed_format_format_hsize
     \fi
   \else
     \enforced\let\\\pack_framed_hboxed_newline
     \enforced\let\hairline\pack_framed_hboxed_hairline
     \let\next\pack_framed_format_format_no_size
   \fi
   \pack_framed_check_extra_offsets
   \edef\p_framed_background{\framedparameter\c!background}%
 % \ifempty\p_framed_background
 %   \let\pack_framed_forgetall\forgetall
 % \else
 %   \let\pack_framed_forgetall\relax
 %   \forgetall
 % \fi
   \edef\framedwidth {\the\ifdim\d_framed_width >\zeropoint         \d_framed_width \else\zeropoint\fi}% public
   \edef\framedheight{\the\ifdim\d_framed_height>\zeropoint         \d_framed_height\else\zeropoint\fi}% public
   \edef\framedoffset{\the\dimexpr\ifconditional\c_framed_has_offset\localoffset    \else\zeropoint\fi}% public
   % for now: i need to think of something better
 % \bitwiseflip \normalizelinemode -\clipwidthnormalizecode % now handled in analyzer
   % but that's for later
   \ifempty\p_framed_orientation
     \let\pack_framed_stop_orientation\relax
   \else
     \pack_framed_start_orientation
   \fi
   \afterassignment\pack_framed_restart
   \setbox\b_framed_normal\next}

% alternatives for width, height, strut and offset

\installcorenamespace{framedwidthalternative}
\installcorenamespace{framedheightalternative}
\installcorenamespace{framedstrutalternative}
\installcorenamespace{framedoffsetalternative}

% widths
%
% \inframed[adaptive=0500]{Just some words}
% \inframed[adaptive=0]   {Just some words}
% \inframed[adaptive=-500]{Just some words}

\defcsname\??framedwidthalternative\empty\endcsname
  {\ifconditional\c_framed_has_format
     \c_framed_has_width\conditionaltrue
     \d_framed_width\hsize
   \else
     \c_framed_has_width\conditionalfalse
     \d_framed_width\zeropoint
   \fi}

\defcsname\??framedwidthalternative\v!fit\endcsname
  {\ifconditional\c_framed_has_format
     \c_framed_has_width\conditionaltrue
     \d_framed_width\hsize
   \else
     \c_framed_has_width\conditionalfalse
     \d_framed_width\zeropoint
   \fi}

\defcsname\??framedwidthalternative\v!fixed\endcsname % equals \v!fit but no shapebox
  {\ifconditional\c_framed_has_format
     \c_framed_has_width\conditionaltrue
     \d_framed_width\hsize
   \else
     \c_framed_has_width\conditionalfalse
     \d_framed_width\zeropoint
   \fi}

\defcsname\??framedwidthalternative\v!broad\endcsname
  {\c_framed_has_width\conditionaltrue
   \d_framed_width\hsize}

\defcsname\??framedwidthalternative\v!max\endcsname % idem broad
  {\c_framed_has_width\conditionaltrue
   \d_framed_width\hsize}

\defcsname\??framedwidthalternative\v!local\endcsname
  {\c_framed_has_width\conditionaltrue
   %\setlocalhsize
   \d_framed_width\localhsize}

\defcsname\??framedwidthalternative\s!unknown\endcsname
  {\c_framed_has_width\conditionaltrue
   \d_framed_width\localwidth}

\def\framed_width_alternative_unknown
  {\c_framed_has_width\conditionaltrue
   \d_framed_width\localwidth}

% heights

\defcsname\??framedheightalternative\empty\endcsname
  {\c_framed_has_height\conditionalfalse
   \d_framed_height\zeropoint}

\defcsname\??framedheightalternative\v!fit\endcsname
  {\c_framed_has_height\conditionalfalse
   \d_framed_height\zeropoint}

\defcsname\??framedheightalternative\v!broad\endcsname
  {\c_framed_has_height\conditionalfalse
   \d_framed_height\zeropoint}

\defcsname\??framedheightalternative\v!max\endcsname
  {\c_framed_has_height\conditionaltrue
   \d_framed_height\vsize}

\defcsname\??framedheightalternative\s!unknown\endcsname
  {\c_framed_has_height\conditionaltrue
   \d_framed_height\localheight}

\def\framed_height_alternative_unknown
  {\c_framed_has_height\conditionaltrue
   \d_framed_height\localheight}

% struts (use let instead?)

\defcsname\??framedstrutalternative\v!no\endcsname
  {\c_framed_has_strut\plusone}

\defcsname\??framedstrutalternative\v!global\endcsname
  {\setstrut}

\defcsname\??framedstrutalternative\v!local\endcsname
  {\setfontstrut}

\defcsname\??framedstrutalternative\v!yes\endcsname
  {\setstrut}

\defcsname\??framedstrutalternative\s!unknown\endcsname
  {\setstrut}

\def\framed_strut_alternative_unknown
  {\setstrut}

\defcsname\??framedstrutalternative\v!none\endcsname % not even pseudo struts
  {\c_framed_has_strut\zerocount}

% offsets

\defcsname\??framedoffsetalternative\v!none\endcsname
  {\c_framed_has_offset\conditionalfalse
   \c_framed_has_strut\plusone
   \c_framed_is_overlaid\conditionalfalse
   \d_framed_local_offset\d_framed_linewidth}

\defcsname\??framedoffsetalternative\v!overlay\endcsname
  {% \ifx\p_framed_frame\v!no \c_framed_has_frame\conditionalfalse \fi % test first
   \c_framed_has_offset\conditionalfalse
   \c_framed_has_strut\plusone
   \c_framed_is_overlaid\conditionaltrue
   \d_framed_local_offset\zeropoint}

% \defcsname\??framedoffsetalternative\v!strut\endcsname
%   {\c_framed_has_offset\conditionalfalse
%    \c_framed_has_strut\plustwo
%    \c_framed_is_overlaid\conditionaltrue
%    \d_framed_local_offset\zeropoint}

\defcsname\??framedoffsetalternative\v!default\endcsname % new per 2-6-2000
  {\c_framed_has_offset\conditionaltrue
   \c_framed_has_strut\plustwo
   \c_framed_is_overlaid\conditionalfalse
   \let\localoffset\defaultframeoffset
   \letframedparameter\c!offset\defaultframeoffset % brrr
   \d_framed_local_offset\dimexpr\localoffset+\d_framed_linewidth\relax}

\def\framed_offset_alternative_unknown
  {\c_framed_has_offset\conditionaltrue
   \c_framed_has_strut\plustwo
   \c_framed_is_overlaid\conditionalfalse
   \let\defaultframeoffset\localoffset
   \d_framed_local_offset\dimexpr\localoffset+\d_framed_linewidth\relax}

\letcsname\??framedoffsetalternative\s!unknown\endcsname\framed_offset_alternative_unknown

% so far for alternatives

\let\pack_framed_stop_orientation\relax

\def\pack_framed_restart
  {\aftergroup\pack_framed_finish}

\def\pack_framed_do_top
  {\raggedtopcommand
   \framedparameter\c!top
   \edef\p_blank{\framedparameter\c!blank}%
   \ifx\p_blank\v!yes\else % auto or no
     \doinhibitblank
   \fi}

\def\pack_framed_do_bottom
  {\framedparameter\c!bottom
   \raggedbottomcommand}

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

% %D \macros
% %D   {doassigncheckedframeoffset}
% %D
% %D Offset helper (see menus):
%
% \def\doassigncheckedframeoffset#1#2% could be a fast \csname .. \endcsname
%   {\edef\checkedframeoffset{#2}%
%    #1%
%      \ifempty\checkedframeoffset      \zeropoint\orelse
%      \ifx\checkedframeoffset\v!overlay\zeropoint\orelse
%      \ifx\checkedframeoffset\v!none   \zeropoint\orelse
%      \ifx\checkedframeoffset\v!frame  \zeropoint\orelse
%      \ifx\checkedframeoffset\v!default\zeropoint\else
%        #2%
%      \fi
%    \relax}
%
% \def\doassigncheckedframeoffset#1#2% could be a fast \csname .. \endcsname
%   {#1\ifchkdimension#2\or\lastchkdimension\else\zeropoint\fi}%

%D \macros
%D   {ifreshapeframebox}
%D
%D The last few lines tell what to do after the content of the box is collected and
%D passed to the next macro. In the case of a fixed width and centered alignment,
%D the content is evaluated and used to determine the most natural width. The rest
%D 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

% experiment ... \p_framed_franalyze -> we could support 'first' as location key
% option but then we will always do an analysis and reimplement the location
% options (btw, beware of location settings of derived functionality that bleed
% into this

\def\pack_framed_profile_box
  {\profilegivenbox\p_profile\b_framed_normal
   \setbox\b_framed_normal\vpack{\unvbox\b_framed_normal}}

\def\pack_framed_reverse_box
  {\ifvbox\b_framed_normal
%      \edef\p_linedirection{\framedparameter\c!linedirection}%
%      \ifx\p_linedirection\v!reverse
     \ifcstok{\framedparameter\c!linedirection}\v!reverse
       \reversevboxcontent\b_framed_normal
       \setbox\b_framed_normal\vpack{\unvbox\b_framed_normal}%
     \fi
   \fi}

\def\pack_framed_finish_a
  {\ifreshapeframebox
     \pack_framed_reshape_process
   \orelse\ifx\p_framed_franalyze\v!yes
     \pack_framed_reshape_analyze
   \else
     \pack_framed_reshape_reset
   \fi
   \c_framed_has_width\conditionalfalse}

\def\pack_framed_finish_b
  {\ifx\p_framed_franalyze\v!yes
     \pack_framed_reshape_analyze
   \else
     \pack_framed_reshape_reset
   \fi
   \c_framed_has_width\conditionalfalse}

\def\pack_framed_finish_c
  {\ifx\p_framed_franalyze\v!yes
     \pack_framed_reshape_analyze
   \else
     \pack_framed_reshape_reset
   \fi}

% Musical timestamp for the adding the "freezespacing" feature: Porcupine Tree -
% Herd Culling (Single Edit - Official Visualiser : some old sql code scrolling by
% on some paper terminal font); scalefactors are in the range -1000..1000.

\protected\def\pack_framed_finish
  {\ifempty{\framedparameter\c!freezespacing}\else
     \boxadapt\b_framed_normal\p_freeze\relax
   \fi
 % \ifcstok{\framedparameter\c!limit}\v!yes
 %   \boxlimit\b_framed_normal % maybe
 % \fi
   \pack_framed_locator_before\p_framed_location
   \ifconditional\c_framed_has_format
     \ifempty\p_framed_anchoring\else
       \pack_framed_reverse_box
     \fi
     \ifx\p_framed_autowidth\v!force
       \pack_framed_finish_a
     \orelse\ifx\localwidth\v!fit
       \ifx\p_framed_autowidth\v!yes
         \pack_framed_finish_a
       \else
         \pack_framed_finish_b
       \fi
     \orelse\ifx\localwidth\v!fixed
       \pack_framed_finish_b
     \else
       \pack_framed_finish_c
     \fi
     \ifconditional\c_framed_has_height \else
       \edef\p_profile{\framedparameter\c!profile}%
       \ifempty\p_profile\else
         \pack_framed_profile_box
       \fi
     \fi
     \ifconditional\page_postprocessors_needed_box
       % quite late
       \page_postprocessors_linenumbers_box\b_framed_normal
     \fi
   \else
     \pack_framed_finish_c
   \fi
   \ifconditional\c_framed_has_width
     \wd\b_framed_normal\d_framed_width
   \fi
   \ifconditional\c_framed_has_height
     \ht\b_framed_normal\d_framed_height
   \else
     \edef\p_framed_minheight{\framedparameter\c!minheight}%
     \ifempty\p_framed_minheight \orelse \ifdim\ht\b_framed_normal<\p_framed_minheight
       \ht\b_framed_normal\p_framed_minheight
     \fi
   \fi
   \edef\p_framed_empty{\framedparameter\c!empty}%
   \ifx\p_framed_empty\v!yes
     \pack_framed_fake_box
   \fi
   \ifempty\p_framed_anchoring\else
     \pack_framed_handle_anchoring
   \fi
   \pack_framed_stop_orientation % moved here at 2014-05-25
   \iftrialtypesetting \else
     \edef\m_overlay_region{\framedparameter\c!region}%
     \ifempty\m_overlay_region\else
       \pack_framed_set_region
     \fi
   \fi
   \d_framed_applied_offset
     \ifconditional\c_framed_is_overlaid
       \zeropoint
     \else
       \d_framed_linewidth
     \fi
   \ifconditional\c_framed_has_offset
     \advanceby\d_framed_applied_offset\localoffset\relax
   \fi
   \ifconditional\c_framed_has_extra_offset
     \pack_framed_apply_extra_offsets % includes \d_framed_applied_offset
   \orelse\ifzeropt\d_framed_applied_offset
   \else
     \pack_framed_widen_box
   \fi
   %
   \ifrelax\postprocessframebox \else
     % better: \pushmacro\\postprocessframebox etc
     \let\next\postprocessframebox
     \let\postprocessframebox\relax % prevent nesting
     \next\b_framed_normal
   \fi
   \iftrialtypesetting
     % new
   \else
     \ifconditional\c_framed_has_frame % real or invisible frame
       \pack_framed_add_outline
     \fi
     \ifempty\p_framed_background \else
       \edef\p_framed_component{\framedparameter\c!component}%
       \pack_framed_add_background
     \fi
   \fi
%    \pack_framed_pop_local_anchors
   \pack_framed_locator_after\p_framed_location
   \iftrialtypesetting \orelse \ifempty\m_overlay_region \else
     \pack_framed_add_region
   \fi
   \box\b_framed_normal
   \global\frameddimensionstate % global so to be used directly afterwards !
     \ifconditional\c_framed_has_width
       \ifconditional\c_framed_has_height \plusthree \else \plusone   \fi
     \else
       \ifconditional\c_framed_has_height \plustwo   \else \zerocount \fi
     \fi
   \egroup
   \egroup}

%D Anchoring is experimental and was prototyped around the ctx meeting in 2018 but
%D never mede it into the core yet. It operates independent of the orientation
%D mechanism already present. It's a rather efficient feature. Best is to use
%D predefined orientations, like:
%D
%D \starttyping
%D \defineorientation[leftflushleft] [orientation=left,horizontal=flushleft]
%D
%D \framed [anchoring={leftflushleft}] {anchoring}
%D \stoptyping
%D
%D But this also works:
%D
%D \starttyping
%D \framed [anchoring={flushleft,top,up}] {anchoring}
%D \stoptyping
%D
%D When an anchoring is given (use \type {normal} for the default) you can also
%D use the \type {xanchor} and \type {yanchor} offsets.

% because we mess with the width later on, we need double wrapping:

\def\pack_framed_handle_anchoring
  {\scratchcounter\autoorientation\p_framed_anchoring\relax
   \edef\p_xanchor{\framedparameter\c!xanchor}%
   \edef\p_yanchor{\framedparameter\c!yanchor}%
   \setbox\b_framed_normal\hpack{\hpack % here
      % using the keyword approachs works ok, don't mess with orientation
      % directly here using \boxorientation ... it doesn't work that well
      \s!orientation\scratchcounter
      \ifempty\p_xanchor\else \s!xoffset \p_xanchor\fi
      \ifempty\p_yanchor\else \s!yoffset \p_yanchor\fi
     {\box\b_framed_normal}}}

\installcorenamespace{framedlocatorbefore}
\installcorenamespace{framedlocatorafter}

\newconstant\frameddimensionstate % global state: 0=unknown 1=width 2=height 3=both

\def\pack_framed_fake_box
  {\setbox\scratchbox\emptyhbox
   \wd\scratchbox\wd\b_framed_normal
   \ht\scratchbox\ht\b_framed_normal
   \dp\scratchbox\dp\b_framed_normal
   \setbox\b_framed_normal\box\scratchbox}

\permanent\protected\def\installframedlocator#1#2#3%
  {\defcsname\??framedlocatorbefore#1\endcsname{#2}%
   \defcsname\??framedlocatorafter #1\endcsname{#3}}

\def\pack_framed_locator_before#1{\begincsname\??framedlocatorbefore#1\endcsname}
\def\pack_framed_locator_after #1{\begincsname\??framedlocatorafter #1\endcsname}

\newdimension\d_framed_locator_ht
\newdimension\d_framed_locator_dp
\newdimension\d_framed_locator_lo
\newdimension\d_framed_locator_ro

\def\pack_framed_locator_set#1%
  {\d_framed_locator_ht\dimexpr
     #1+\d_framed_linewidth
     \ifconditional\c_framed_has_offset
       +\framedparameter\c!offset
     \fi
     +\framedparameter\c!toffset
   \relax
   \d_framed_locator_dp\dimexpr\ht\b_framed_normal-\d_framed_locator_ht\relax}

\def\pack_framed_locator_set_lo
  {\global\d_framed_locator_lo\dimexpr
     \d_framed_linewidth
     \ifconditional\c_framed_has_offset
       +\framedparameter\c!offset
     \fi
     +\framedparameter\c!loffset
   \relax}

\def\pack_framed_locator_set_ro
  {\global\d_framed_locator_ro\dimexpr
     \d_framed_linewidth
     \ifconditional\c_framed_has_offset
       +\framedparameter\c!offset
     \fi
     +\framedparameter\c!roffset
   \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}
% \vskip2cm
% \ruledhbox
%  {A
%   \framed[width=2cm,align=middle,location=keep]   {location\\equals\\top}
%   \framed[width=2cm,align=middle,location=formula {location\\equals\\bottom}
%   B}

\installframedlocator \v!hanging % best with strut=no *1* / see mail to list by SB
  {}
  {\scratchdimen\ht\b_framed_normal
   \setbox\b_framed_normal\hpack{\lower\scratchdimen\box\b_framed_normal}%
   \dp\b_framed_normal\scratchdimen
   \ht\b_framed_normal\zeropoint
   \box\b_framed_normal}

\installframedlocator \v!depth % *1*
  {}
  {\setbox\b_framed_normal\hpack{\lower\strutdp\box\b_framed_normal}%
   \ht\b_framed_normal\dimexpr\ht\b_framed_normal-\strutdp\relax
   \dp\b_framed_normal\strutdp
   \box\b_framed_normal}

\installframedlocator \v!height % *1*
  {}
  {\scratchdimen\dimexpr \ht\b_framed_normal - \strutht \relax
   \setbox\b_framed_normal\hpack{\lower\scratchdimen\box\b_framed_normal}%
   \dp\b_framed_normal\dimexpr\ht\b_framed_normal-\strutht\relax
   \ht\b_framed_normal\strutht
   \box\b_framed_normal}

\installframedlocator \v!high
  {}
  {\pack_framed_locator_set\strutht
   \setbox\b_framed_normal\hpack{\lower\d_framed_locator_dp\box\b_framed_normal}%
   \ht\b_framed_normal\strutht
   \dp\b_framed_normal\strutdp
   \hpack{\box\b_framed_normal}} %  why do we pack .. dange of loosing?

\installframedlocator \v!line
  {}
  {\setbox\b_framed_normal\hpack{\lower.5\ht\b_framed_normal\box\b_framed_normal}%
   \ht\b_framed_normal.5\lineheight
   \dp\b_framed_normal.5\lineheight
   \hpack{\box\b_framed_normal}} %  why do we pack .. dange of loosing?

\installframedlocator \v!low
  {}
  {\pack_framed_locator_set\strutdp
   \setbox\b_framed_normal\hpack{\lower\d_framed_locator_ht\box\b_framed_normal}%
   \ht\b_framed_normal\strutht
   \dp\b_framed_normal\strutdp
   \box\b_framed_normal}

\installframedlocator \v!top
  {}
  {\pack_framed_locator_set\strutht
   \setbox\b_framed_normal\hpack{\lower\d_framed_locator_dp\box\b_framed_normal}%
   \ht\b_framed_normal\d_framed_locator_ht
   \dp\b_framed_normal\d_framed_locator_dp
   \hpack{\box\b_framed_normal}} %  why do we pack .. dange of loosing?

\installframedlocator \v!middle
  {}
  {\scratchdimen.5\ht\b_framed_normal
   \setbox\b_framed_normal\hpack{\lower\scratchdimen\box\b_framed_normal}%
   \ht\b_framed_normal\scratchdimen
   \dp\b_framed_normal\scratchdimen
   \hpack{\box\b_framed_normal}} %  why do we pack .. dange of loosing?

\installframedlocator \v!lohi % maybe also \v!center
  {\pack_framed_locator_before\v!middle}
  {\pack_framed_locator_after \v!middle}

\installframedlocator \v!bottom
  {}
  {\pack_framed_locator_set\strutdp
   \setbox\b_framed_normal\hpack{\lower\d_framed_locator_ht\box\b_framed_normal}%
   \ht\b_framed_normal\d_framed_locator_dp
   \dp\b_framed_normal\d_framed_locator_ht
   \hpack{\box\b_framed_normal}} %  why do we pack .. dange of loosing?

\installframedlocator \v!keep % retains height/depth
  {\pack_framed_remove_depth}
  {\pack_framed_restore_depth}

\newdimension\d_framed_formula

\installframedlocator \v!formula % private, will become a more generic name
  {}
  {\pack_framed_locator_set\d_framed_formula
   \setbox\b_framed_normal\hpack{\lower\d_framed_locator_dp\box\b_framed_normal}%
   \ht\b_framed_normal\d_framed_locator_ht
   \dp\b_framed_normal\d_framed_locator_dp
   \hpack{\box\b_framed_normal}} %  why do we pack .. danger of loosing?

\installframedlocator \v!inline
  {}
  {\scratchoffset\dimexpr\strutdp+\d_framed_applied_offset\relax
   \boxyoffset\b_framed_normal\dimexpr\boxyoffset\b_framed_normal-\scratchoffset\relax
   \ht\b_framed_normal\dimexpr\ht\b_framed_normal-\scratchoffset\relax
   \dp\b_framed_normal\dimexpr\strutdp+\d_framed_applied_offset\relax
   \box\b_framed_normal}

% also used in fastlocalframed

\newdimension\d_framed_original_wd
\newdimension\d_framed_original_ht
\newdimension\d_framed_original_dp

\def\pack_framed_remove_depth
  {\d_framed_original_wd\wd\b_framed_normal
   \d_framed_original_ht\ht\b_framed_normal
   \d_framed_original_dp\dp\b_framed_normal
   \ifzeropt\d_framed_original_dp\else
     \setbox\b_framed_normal\hpack{\raise\d_framed_original_dp\box\b_framed_normal}%
   \fi
   \wd\b_framed_normal\d_framed_original_wd
   \ht\b_framed_normal\dimexpr\d_framed_original_ht+\d_framed_original_dp\relax
   \dp\b_framed_normal\zeropoint}

\def\pack_framed_restore_depth
  {\ifzeropt\d_framed_original_dp \else
     \setbox\b_framed_normal\hpack{\lower\d_framed_original_dp\box\b_framed_normal}%
   \fi
   \wd\b_framed_normal\d_framed_original_wd
   \ht\b_framed_normal\d_framed_original_ht
   \dp\b_framed_normal\d_framed_original_dp}

% \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\pack_framed_start_orientation
  {\ifcase\p_framed_orientation
     \let\pack_framed_stop_orientation\relax
   \else
     \let\pack_framed_stop_orientation\pack_framed_stop_orientation_indeed
   \fi}

\def\pack_framed_stop_orientation_indeed
  {\setbox\b_framed_normal\hpack{\dorotatebox\p_framed_orientation\hpack{\box\b_framed_normal}}%
   \d_framed_height\ht\b_framed_normal
   \d_framed_width \wd\b_framed_normal}

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

\permanent\tolerant\protected\def\inframed[#S#1]{\framed[\c!location=\v!low,#1]}

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

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

\newdimension\d_framed_applied_offset
\newdimension\d_framed_loffset
\newdimension\d_framed_roffset
\newdimension\d_framed_toffset
\newdimension\d_framed_boffset

\def\pack_framed_check_extra_offsets % we could check h and v indepently
  {\c_framed_has_extra_offset\conditionalfalse
   \d_framed_loffset\framedparameter\c!loffset\relax
   \d_framed_roffset\framedparameter\c!roffset\relax
   \d_framed_toffset\framedparameter\c!toffset\relax
   \d_framed_boffset\framedparameter\c!boffset\relax
   \ifzeropt\d_framed_loffset\else \advanceby\d_framed_width -\d_framed_loffset \c_framed_has_extra_offset\conditionaltrue \fi
   \ifzeropt\d_framed_roffset\else \advanceby\d_framed_width -\d_framed_roffset \c_framed_has_extra_offset\conditionaltrue \fi
   \ifzeropt\d_framed_toffset\else \advanceby\d_framed_height-\d_framed_toffset \c_framed_has_extra_offset\conditionaltrue \fi
   \ifzeropt\d_framed_boffset\else \advanceby\d_framed_height-\d_framed_boffset \c_framed_has_extra_offset\conditionaltrue \fi}

\def\pack_framed_apply_extra_offsets
  {\setbox\b_framed_normal\vpack\bgroup
     \advanceby\d_framed_toffset\d_framed_applied_offset
     \advanceby\d_framed_boffset\d_framed_applied_offset
     \advanceby\d_framed_loffset\d_framed_applied_offset
     \advanceby\d_framed_roffset\d_framed_applied_offset
     \kern\d_framed_toffset
     \hpack\bgroup
        \kern\d_framed_loffset
        \box\b_framed_normal
        \kern\d_framed_roffset
     \egroup
     \kern\d_framed_boffset
   \egroup}

\def\pack_framed_widen_box
  {\setbox\b_framed_normal\vpack
     {\kern\d_framed_applied_offset
      \hpack{\kern\d_framed_applied_offset\box\b_framed_normal\kern\d_framed_applied_offset}%
      \kern\d_framed_applied_offset}}

%D Let's hope that the next few examples show us enough of what needs to be
%D 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=no,offset=.5cm]   {rule based learning}
%D \framed[strut=no,offset=0cm]    {rule based learning}
%D \framed[strut=no,offset=none]   {rule based learning}
%D \framed[strut=no,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 between borders with
%D straight lines and those with round corners. When using the first alternative it
%D is possible to turn off one or more lines. More fancy shapes are also possible by
%D specifying dedicated backgrounds. Turning lines on and off is implemented as
%D efficient as possible and as a result is interface language dependant. This next
%D implementation evolved from simpler ones. It puts for instance the rules on top
%D of the content and provides additional offset capabilities. The lot of calls to
%D other macros makes this mechanism not that easy to comprehend.
%D
%D We handle left, right or middle alignment as well as fixed or free widths and
%D heights. Each combination gets its own macro.
%D
%D The following code handles one-liners: \type {align={line,flushright}}. Beware,
%D since we entered a group and either or not grab the next bgroup token, we need to
%D finish the group in the oneliner mode.

\ifdefined\raggedonelinerstate \else \newconditional\raggedonelinerstate \fi

\permanent\protected\def\doformatonelinerbox % beware: assumes explicit preceding bgroup
  {\ifconditional\raggedonelinerstate
     \expandafter\dodoformatonelinerbox
   \else
     \expandafter\nodoformatonelinerbox
   \fi}

\permanent\protected\def\dodoformatonelinerbox
  {\afterassignment\redoformatonelinerbox
   \setbox\nextbox\hbox} % maybe \hpack

\permanent\protected\def\redoformatonelinerbox
  {\aftergroup\dododoformatonelinerbox
   \ignorespaces}

\permanent\protected\def\dododoformatonelinerbox
  {\hpack to \hsize % was \hbox
     {\ifcase\raggedstatus\or\hss\or\hss       \fi
      \unhbox\nextbox \removeunwantedspaces
      \ifcase\raggedstatus\or    \or\hss\or\hss\fi}%
   \egroup}

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

%D The handlers:

% Beware, we have a \noindent so an empty line is indeed an empty line and
% the \synchronizeinlinedirection triggers a vbox instead of a line.
%
% \startTEXpage[offset=0.5ex,align={lohi,middle}]
%
%     \vbox{\hbox{x}}
% \stopTEXpage
%
% \startTEXpage[offset=0.5ex,align={lohi,middle}]
%     \vbox{\hbox{x}}
% \stopTEXpage

% \def\pack_framed_forgetall{\forgetall}

\def\pack_framed_set_foregroundcolor
  {\edef\p_framed_foregroundcolor{\framedparameter\c!foregroundcolor}%
   \ifempty\p_framed_foregroundcolor\else\dousecolorparameter\p_framed_foregroundcolor\fi}

\def\pack_framed_do_setups
  {\ifempty\p_framed_setups \else
     \setups[\p_framed_setups]% \texsetup (or only one!)
   % \fastsetup\p_framed_setup % singular would have been better
   \fi}

\def\pack_framed_format_format_yes
  {\vbox to \d_framed_height \p_framed_adaptive
     \bgroup
       \let\postprocessframebox\relax
     % \pack_framed_forgetall
       \iftrialtypesetting \else
         \pack_framed_set_foregroundcolor
       \fi
       \oninterlineskip
       \hsize\d_framed_width
       \vsize\d_framed_height
       \pack_framed_do_setups
       \raggedcommand
       \pack_framed_do_top
       \synchronizeinlinedirection
       \localbegstrut
       \atendofgroup\localendstrut
       \atendofgroup\pack_framed_do_bottom
       \doformatonelinerbox}

\def\pack_framed_format_format_nop
  {\vbox to \d_framed_height
     \bgroup
       \let\postprocessframebox\relax
     % \pack_framed_forgetall
       \iftrialtypesetting \else
         \pack_framed_set_foregroundcolor
       \fi
       \oninterlineskip
       \hsize\d_framed_width
       \vsize\d_framed_height
       \pack_framed_do_setups
       \raggedcenter
       \vss
       \synchronizeinlinedirection
       \localbegstrut
       \atendofgroup\localendstrut
       \atendofgroup\vss
       \doformatonelinerbox}

\def\pack_framed_format_format_height
  {\vbox to \d_framed_height
     \bgroup
       \let\postprocessframebox\relax
     % \pack_framed_forgetall
       \iftrialtypesetting \else
         \pack_framed_set_foregroundcolor
       \fi
       \oninterlineskip
       \pack_framed_do_setups
       \raggedcommand
       \vss
       \synchronizeinlinedirection
       \localbegstrut
       \atendofgroup\localendstrut
       \atendofgroup\vss
       \doformatonelinerbox}

\def\pack_framed_format_format_width
  {\vbox
     \bgroup
       \let\postprocessframebox\relax
     % \pack_framed_forgetall
       \iftrialtypesetting \else
         \pack_framed_set_foregroundcolor
       \fi
       \oninterlineskip
       \hsize\d_framed_width
       \pack_framed_do_setups
       \raggedcommand
       \pack_framed_do_top
       \synchronizeinlinedirection
       \localbegstrut
       \atendofgroup\localendstrut
       \atendofgroup\pack_framed_do_bottom
       \doformatonelinerbox}

\def\pack_framed_format_format_vsize
  {\vbox to \d_framed_height % no vpack .. maybe grid
     \bgroup
       \let\postprocessframebox\relax
     % \pack_framed_forgetall
       \iftrialtypesetting \else
         \pack_framed_set_foregroundcolor
       \fi
       \vsize\d_framed_height
       \pack_framed_do_setups
       \vss
       \atendofgroup\vss
       \hbox
         \bgroup
         \aftergroup\egroup
         \synchronizeinlinedirection
         \localstrut
         \doformatonelinerbox}

\def\pack_framed_format_format_hsize
  {\hbox to \d_framed_width
     \bgroup
       \let\postprocessframebox\relax
     % \pack_framed_forgetall
       \iftrialtypesetting \else
         \pack_framed_set_foregroundcolor
       \fi
       \pack_framed_do_setups
       \hss
       \synchronizeinlinedirection
       \localstrut
       \atendofgroup\hss
       \doformatonelinerbox}

\def\pack_framed_format_format_no_size
  {\hbox
     \bgroup
       \iftrialtypesetting \else
         \pack_framed_set_foregroundcolor
       \fi
       \let\postprocessframebox\relax
       \pack_framed_do_setups
       \synchronizeinlinedirection
       \localstrut
       \doformatonelinerbox}

%D On the next page we show some examples of how these macros come into action. The
%D examples show us how \type {fit}, \type {broad} dimensions influence the
%D formatting. Watch the visualized struts. \footnote {Here we used \type
%D {\showstruts}.}
%D
%D \startpostponing
%D \bgroup
%D \showstruts
%D \dontcomplain
%D \starttabulate[|c|c|c|c|c|c|]
%D % \HL
%D \NC \framed[width=.2\hsize, height=.2\hsize, align=]       {a\endgraf b\endgraf c}
%D \NC \framed[width=.2\hsize, height=broad,    align=]       {a\endgraf b\endgraf c}
%D \NC \framed[width=.2\hsize, height=fit,      align=]       {a\endgraf b\endgraf c}
%D \NC \framed[width=fit,      height=.2\hsize, align=]       {a\endgraf b\endgraf c}
%D \NC \framed[width=fit,      height=broad,    align=]       {a\endgraf b\endgraf c}
%D \NC \framed[width=fit,      height=fit,      align=]       {a\endgraf b\endgraf c}
%D \NC \NR
%D % \HL
%D \NC \framed[width=.2\hsize, height=.2\hsize, align=yes]    {a\endgraf b\endgraf c}
%D \NC \framed[width=.2\hsize, height=broad,    align=yes]    {a\endgraf b\endgraf c}
%D \NC \framed[width=.2\hsize, height=fit,      align=yes]    {a\endgraf b\endgraf c}
%D \NC \framed[width=fit,      height=.2\hsize, align=yes]    {a\endgraf b\endgraf c}
%D \NC \framed[width=fit,      height=broad,    align=yes]    {a\endgraf b\endgraf c}
%D \NC \framed[width=fit,      height=fit,      align=yes]    {a\endgraf b\endgraf c}
%D \NC \NR
%D % \HL
%D \NC \framed[width=.2\hsize, height=.2\hsize, align=right]  {a\endgraf b\endgraf c}
%D \NC \framed[width=.2\hsize, height=broad,    align=right]  {a\endgraf b\endgraf c}
%D \NC \framed[width=.2\hsize, height=fit,      align=right]  {a\endgraf b\endgraf c}
%D \NC \framed[width=fit,      height=.2\hsize, align=right]  {a\endgraf b\endgraf c}
%D \NC \framed[width=fit,      height=broad,    align=right]  {a\endgraf b\endgraf c}
%D \NC \framed[width=fit,      height=fit,      align=right]  {a\endgraf b\endgraf c}
%D \NC \NR
%D % \HL
%D \NC \framed[width=.2\hsize, height=.2\hsize, align=left]   {a\endgraf b\endgraf c}
%D \NC \framed[width=.2\hsize, height=broad,    align=left]   {a\endgraf b\endgraf c}
%D \NC \framed[width=.2\hsize, height=fit,      align=left]   {a\endgraf b\endgraf c}
%D \NC \framed[width=fit,      height=.2\hsize, align=left]   {a\endgraf b\endgraf c}
%D \NC \framed[width=fit,      height=broad,    align=left]   {a\endgraf b\endgraf c}
%D \NC \framed[width=fit,      height=fit,      align=left]   {a\endgraf b\endgraf c}
%D \NC \NR
%D % \HL
%D \NC \framed[width=.2\hsize, height=.2\hsize, align=middle] {a\endgraf b\endgraf c}
%D \NC \framed[width=.2\hsize, height=broad,    align=middle] {a\endgraf b\endgraf c}
%D \NC \framed[width=.2\hsize, height=fit,      align=middle] {a\endgraf b\endgraf c}
%D \NC \framed[width=fit,      height=.2\hsize, align=middle] {a\endgraf b\endgraf c}
%D \NC \framed[width=fit,      height=broad,    align=middle] {a\endgraf b\endgraf c}
%D \NC \framed[width=fit,      height=fit,      align=middle] {a\endgraf b\endgraf c}
%D \NC \NR
%D % \HL
%D \stoptabulate
%D \egroup
%D \stoppostponing

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

% \newinteger  \framednoflines
% \newdimension\framedfirstheight
% \newdimension\framedlastdepth
% \newdimension\framedminwidth
% \newdimension\framedmaxwidth
% \newdimension\framedaveragewidth

\def\pack_framed_reshape_reset
  {\framednoflines    \zerocount
   \framedfirstheight \zeropoint
   \framedlastdepth   \zeropoint
   \framedminwidth    \zeropoint
   \framedmaxwidth    \zeropoint
   \framedaveragewidth\zeropoint}

\def\pack_framed_reshape_process
  {\ifvbox\b_framed_normal
     \ifconditional\c_pack_resync
       \clf_doresyncframedbox
     \else
       \clf_doreshapeframedbox
     \fi\b_framed_normal\relax
     \ifx\p_framed_adaptive\s!delay
       \adaptivecheckbox\b_framed_normal
     \fi
   \fi}

\def\pack_framed_reshape_analyze
  {\ifvbox\b_framed_normal
     \ifconditional\c_pack_resync
       \clf_doresyncframedbox
     \else
       \clf_doanalyzeframedbox
     \fi\b_framed_normal\relax
   \fi}

% 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 frame as well as the
%D background an additional offset and even a bit more depth. The blue outline is
%D the frame, the red box is the background and the small black outline is the
%D visualization of the resulting box, that is, we applied \type {\ruledhbox} to
%D the result.
%D
%D \startpostponing
%D \bgroup
%D \unprotect
%D \dontcomplain
%D
%D \startbuffer
%D \unprotect
%D \vbox to \vsize
%D   \bgroup
%D     \startalignment[middle]
%D     \vss
%D     \dontleavehmode\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=\the\dimexpr\framedparameter\c!backgroundoffset\relax\\
%D            frameoffset=\the\dimexpr\framedparameter\c!frameoffset\relax\\
%D            depth=\the\dimexpr\framedparameter\c!depth\relax}
%D         \dontleavehmode \ruledhbox{\framed[backgroundoffset=0pt,frameoffset=0pt]{\status}}
%D         \vss
%D         \dontleavehmode \ruledhbox{\framed[backgroundoffset=5pt,frameoffset=0pt]{\status}}
%D         \vss
%D         \dontleavehmode \ruledhbox{\framed[backgroundoffset=0pt,frameoffset=5pt]{\status}}
%D         \vss
%D         \dontleavehmode \ruledhbox{\framed[backgroundoffset=2pt,frameoffset=5pt]{\status}}
%D         \vss
%D         \dontleavehmode \ruledhbox{\framed[backgroundoffset=5pt,frameoffset=2pt]{\status}}
%D         \vss
%D         \dontleavehmode \ruledhbox{\framed[backgroundoffset=5pt,frameoffset=5pt]{\status}}
%D       \egroup
%D     \vss
%D     \stopalignment
%D     \egroup
%D \protect
%D \stopbuffer
%D
%D \getbuffer \page
%D
%D {\setupframed[depth=4pt]\getbuffer} \page
%D
%D \protect
%D \egroup
%D \stoppostponing

%D We can draw lines from left to right and top to bottom by using the normal \type
%D {\hairline} command. Both directions 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 possible to the circumstances
%D and act as natural as possible.

\protected\def\pack_framed_vboxed_hairline % nasty overlay mess .. needed for autowidth
  {\begingroup
   \scratchoffset\ifconditional\c_framed_has_offset \localoffset \else \zeropoint \fi
   \scratchwidth \dimexpr\scratchoffset+\d_framed_linewidth\relax
   \par
   \nointerlineskip
   \kern\scratchoffset
   \dontleavehmode
     \hrule\s!height\d_framed_linewidth\s!depth\zeropoint
   \par
   \kern-\d_framed_linewidth
   \dontleavehmode
     \hpack to \zeropoint{\hss\vrule\s!height\d_framed_linewidth\s!depth\zeropoint\s!width\scratchwidth}%
     \hfill
     \hpack to \zeropoint{\vrule\s!height\d_framed_linewidth\s!depth\zeropoint\s!width\scratchwidth\hss}%
   \par
   \nointerlineskip
   \kern\scratchoffset
   \nointerlineskip
   \endgraf
   \nointerlineskip
   \localbegstrut
   \endgroup}

\protected\def\pack_framed_hboxed_hairline % use framed dimen
  {\bgroup
   \scratchoffset\ifconditional\c_framed_has_offset \localoffset \else \zeropoint \fi
   \ifconditional\c_framed_has_height
     \dimen\scratchheight\dimexpr\localheight/\plustwo+\strutdp-\plustwo\d_framed_linewidth\relax
     \dimen\scratchdepth \dimexpr\localheight/\plustwo-\strutdp+\plustwo\d_framed_linewidth\relax
   \else
     \dimen\scratchheight\dimexpr\strutht+\scratchoffset\relax
     \dimen\scratchdepth \dimexpr\strutdp+\scratchoffset\relax
   \fi
   \unskip
   \setbox\scratchbox\hpack
     {\kern\scratchoffset
      \vrule\s!height\dimen\scratchheight\s!depth\dimen\scratchdepth\s!width\d_framed_linewidth
      \kern\scratchoffset}%
   \ht\scratchbox\strutht
   \dp\scratchbox\strutdp
   \box\scratchbox
   \ignorespaces
   \egroup}

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

\protected\def\pack_framed_vboxed_newline
  {\endgraf\ignorespaces}

\protected\def\pack_framed_hboxed_newline
  {\unskip\normalspace\ignorespaces}

%D We can set each rule on or off. The default setting is inherited from
%D \type {frame}. An earlier implementation use a bit different approach, but the new
%D one seems more 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   {startframedtext, setupframedtexts, defineframedtext}
%D
%D The general framing command we discussed previously, is not entirely suited for
%D what we call framed texts, as for instance used in intermezzo's. The next
%D examples show what 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 as well as the
%D 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.

\installcorenamespace{framedtext}
\installcorenamespace{framedtextlocation}

\installframedcommandhandler \??framedtext {framedtext} \??framedtext

\let\setupframedtexts\setupframedtext

\setupframedtext
  [\c!width=.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!orientation=,
  %\c!indenting=,
  %\c!foregroundcolor=,
  %\c!foregroundstyle=,
  %\c!background=,
  %\c!backgroundcolor=,
   \c!linecorrection=\v!on,
   \c!depthcorrection=\v!on,
   \c!margin=\v!standard]

\appendtoks
    \frozen\instance\protected\edefcsname\e!start\currentframedtext\endcsname{\pack_framed_text_start [\currentframedtext]}%
    \frozen\instance\protected\edefcsname\e!stop \currentframedtext\endcsname{\pack_framed_text_stop                      }%
    \frozen\instance\protected\edefcsname        \currentframedtext\endcsname{\pack_framed_text_direct[\currentframedtext]}%
\to \everydefineframedtext

\defcsname\??framedtextlocation\v!left\endcsname
  {\letframedtextparameter\c!left \relax
   \letframedtextparameter\c!right\hfill}

\defcsname\??framedtextlocation\v!right\endcsname
  {\letframedtextparameter\c!left \hfill
   \letframedtextparameter\c!right\relax}

\defcsname\??framedtextlocation\v!middle\endcsname
  {\letframedtextparameter\c!left \hfill
   \letframedtextparameter\c!right\hfill}

\defcsname\??framedtextlocation\v!none\endcsname
  {\letframedtextparameter\c!left \relax
   \letframedtextparameter\c!right\relax
   \c_framed_text_location_none\conditionaltrue}

\tolerant\protected\def\pack_framed_text_start[#1]#*[#S#2]#*[#S#3]% or #1#*[#2]#*[#3]% and pass {#1}
  {\bgroup
   \cdef\currentframedtext{#1}%
   \doifelseassignment{#2}%
     {\pack_framed_text_start_indeed\empty{#2}}
     {\pack_framed_text_start_indeed{#2}{#3}}}

% todo: sort out first/lastline ht/dp . . will be a more advanced mechanism some day (soon)

\def\pack_framed_text_start_indeed#1#2%
  {\setupframedtexts[\currentframedtext][#2]%
   \ifempty{#1}\else
     \setframedtextparameter\c!location{#1}% does not listen to #3
   \fi
   \c_framed_text_location_none\conditionalfalse
   \begincsname\??framedtextlocation\framedtextparameter\c!location\endcsname
   \resetframedtextparameter\c!location
   \pack_framed_text_check
   \setbox\b_framed_normal\vbox % \vpack
     \startboxedcontent
       \hsize\localhsize
    %  \insidefloattrue % ? better
       \usebodyfontparameter\framedtextparameter
    %  \edef\p_framed_text_strut{\letframedtextparameter\c!strut}% to be used
       \letframedtextparameter\c!strut\v!no
       \inheritedframedtextframed\bgroup
         \enforced\let\\\endgraf
         \edef\p_framed_text_depthcorrection{\framedtextparameter\c!depthcorrection}%
         \ifx\p_framed_text_depthcorrection\v!on
            \pack_framed_text_start_depth_correction
         \else
            \bgroup
         \fi
         % no longer: \vskip-\strutdp % brrr why is this needed ... needs to be sorted out, see testcase 1
         \doinhibitblank
         \useindentingparameter\framedtextparameter
         \useframedtextstyleandcolor\c!style\c!color
         \framedtextparameter\c!inner
         \ignorespaces}

% testcase 1:
%
% \showstruts
% \startframedtext[align={normal,tolerant},offset=0pt] \input tufte \stopframedtext
% \startframedtext[align={normal,tolerant},offset=0pt,depthcorrection=off] \input tufte \stopframedtext
% \startframedtext[align={normal,tolerant},offset=0pt,depthcorrection=off] \inframed{x} \stopframedtext
% \framed[align={normal,tolerant},offset=0pt]{\input tufte }

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

\defineplacement[\??framedtext][\s!parent=\??framedtext\currentframedtext]

\protected\def\pack_framed_text_stop % no \baselinecorrection, see faq docs
  {\endgraf
   \removelastskip
   \ifx\p_framed_text_depthcorrection\v!on
     \pack_framed_text_stop_depth_correction
   \else
     \egroup
   \fi
   \stopboxedcontent
   \ifconditional\c_framed_text_location_none
     \egroup
     \box\b_framed_normal
   \orelse\ifinsidefloat
     \egroup
     \box\b_framed_normal
   \else
     \egroup
     \placement[\??framedtext][\c!depthcorrection=\v!off]{\box\b_framed_normal}%
   \fi
   \egroup}

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

\def\pack_framed_text_check % messy dependency
  {\localhsize\hsize
   \ifinsidefloat \orelse \ifdim\d_page_sides_vsize>\zeropoint % also possible: \c_page_sides_checks_done>\zeropoint
   % \strut            % rather clean way to invoke the sidefloat OTR
   % \setbox0=\lastbox % and get the widths set, so from now on we
   % \setlocalhsize    % can have framed texts alongside sidefloats
     \checksidefloat
     \setlocalhsize
   \fi}

\def\pack_framed_text_start_depth_correction
  {\bgroup
   \ifhmode
     \par
   \fi
   \ifvmode
     \verticalstrut
     % we need \nowhitespace in case of setups setting whitespace
     % nb, not safe, text vs \vbox as next
     \vskip-\struttotal
     \nowhitespace
   \fi} % na vskip ! new 20/05/2004, fails with next content being box (\scale{..})

\def\pack_framed_text_stop_depth_correction
  {\ifhmode
     \par
   \fi
   \ifvmode
     \forgetall
     \vskip-\struttotal
     \verticalstrut
     \egroup
     \forgetall % brrr too often
     \vskip-\lineheight
     \verticalstrut
   \else
     \egroup
   \fi}

%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 slightly different
%D and is not aligned.

\let\pack_framed_strut\relax

\tolerant\protected\def\pack_framed_text_direct[#1]#*[#S#2]%
  {\bgroup
   \cdef\currentframedtext{#1}%
   \usebodyfontparameter\framedtextparameter
   \setupcurrentframedtext[#2]%
   \edef\p_framed_text_strut{\framedtextparameter\c!strut}%
   \letframedtextparameter\c!strut\v!no
   \inheritedframedtextframed\bgroup
     \blank[\v!disable]%
     \enforced\let\\\endgraf
     \useframedtextstyleandcolor\c!style\c!color
     % no longer: \vskip-\strutdp % brrr why is this needed ... needs to be sorted out, see testcase 1
     \framedtextparameter\c!inner
     \ifx\p_framed_text_strut\v!no
        \let\pack_framed_strut\relax
     \else
        \let\pack_framed_strut\strut
     \fi
     \bgroup
     \aftergroup\pack_framed_text_stop_direct
     \afterassignment\ignorespaces
     \afterassignment\pack_framed_strut
     \let\next=}

\def\pack_framed_text_stop_direct
  {\removelastskip
   \egroup
   \egroup}

\defineframedtext
  [\v!framedtext]

%D \macros
%D   {defineframed}
%D
%D One can also define simple framed texts, using:
%D
%D \showsetup{defineframed}
%D
%D As suggested by Wolfgang we can now use the new \MKIV\ inheritance model instead
%D of passing a combination of arguments. This also also simplified the \type
%D {\setupframed} command. There are certainly more places where such improvements
%D can be made.

\appendtoks
    \ifcsname\??regularframedlevel\currentframed\endcsname
      % already defined, keeps settings
    \else
      \expandafter\newinteger\csname\??regularframedlevel\currentframed\endcsname
    \fi
\to \everypresetframed

\appendtoks
    \frozen\instance\protected\edefcsname\currentframed\endcsname{\pack_framed_defined_process[\currentframed]}%
\to \everydefineframed

\newinteger\c_temp_framed_crap

% \permanent\tolerant\protected\def\pack_framed_defined_process[#1]#*[#2]% official (not much checking, todo: parent)
%   {\bgroup
%    \ifcsname\??regularframedlevel#1\endcsname
%     %\expandafter\let\expandafter\c_pack_framed_temp\csname\??regularframedlevel#1\endcsname
%      \expandafter\let\expandafter\c_pack_framed_temp\lastnamedcs
%    \else
%      \let\c_pack_framed_temp\c_temp_framed_crap
%    \fi
%    \advanceby\c_pack_framed_temp\plusone
%    \defcsname\??framed#1>\the\c_pack_framed_temp:\s!parent\endcsname{\??framed#1}% \inheritlocalframed
%    \bgroup
%    \cdef\currentframed{#1>\the\c_pack_framed_temp}%
%    \pack_framed_initialize
%    \setupcurrentframed[#2]% here !
%    \pack_framed_process_indeed}

\permanent\tolerant\protected\def\pack_framed_defined_process[#1]#*[#S#2]% official (not much checking, todo: parent)
  {\bgroup
   \ifcsname\??regularframedlevel#1\endcsname
    %\expandafter\let\expandafter\c_pack_framed_temp\csname\??regularframedlevel#1\endcsname
     \scratchcounter\lastnamedcs
   \else
     \scratchcounter\c_temp_framed_crap
   \fi
   \advanceby\scratchcounter\plusone
   \defcsname\??framed#1>\the\scratchcounter:\s!parent\endcsname{\??framed#1}% \inheritlocalframed
   \bgroup
   \cdef\currentframed{#1>\the\scratchcounter}%
   \pack_framed_initialize
   \setupcurrentframed[#2]% here !
   \pack_framed_process_indeed}

\aliased\let\placeframed\pack_framed_defined_process % new per 2012/04/23

%D We can do:
%D
%D \starttyping
%D \defineframed[\v!framed]
%D \stoptyping
%D
%D but the existing one is ok as well (less csname messy too).

%D New, for the moment private; let's see when GB finds out about this one and its
%D 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

\installcorenamespace{framedcontent}

\installframedcommandhandler \??framedcontent {framedcontent} \??framedcontent

\setupframedcontent
  [\c!leftoffset=\zeropoint,
  %\c!rightoffset=\framedcontentparameter\c!leftoffset,
   \c!rightoffset=\scratchleftoffset,
   \c!topoffset=\zeropoint,
  %\c!bottomoffset=\framedcontentparameter\c!topoffset,
   \c!bottomoffset=\scratchtopoffset,
   \c!strut=\v!no,
  %\c!linecorrection=\v!no,
  %\c!left=,
  %\c!right=,
  %\c!width=\v!fit,
   \c!offset=\v!overlay]

\permanent\let\stopframedcontent\relax

\permanent\tolerant\protected\def\startframedcontent[#1]%
  {\bgroup
   \cdef\currentframedcontent{#1}%
   \ifx\currentframedcontent\v!off
     \enforced\let\stopframedcontent\egroup
   \else
     \checkframedcontentparent
     \enforced\let\stopframedcontent\pack_framed_stop_content_indeed
     \expandafter\pack_framed_start_content_indeed
   \fi}

\def\pack_framed_start_content_indeed
  {\setbox\b_framed_normal\hpack\bgroup
     \setlocalhsize
     \hsize\localhsize
     \scratchleftoffset  \framedcontentparameter\c!leftoffset  \relax
     \scratchrightoffset \framedcontentparameter\c!rightoffset \relax
     \scratchtopoffset   \framedcontentparameter\c!topoffset   \relax
     \scratchbottomoffset\framedcontentparameter\c!bottomoffset\relax
     \advanceby\hsize\dimexpr-\scratchleftoffset-\scratchrightoffset \relax
     \advanceby\vsize\dimexpr-\scratchtopoffset -\scratchbottomoffset\relax
     \kern\scratchleftoffset
     \vpack\bgroup
       \vskip\scratchtopoffset
       \vbox\bgroup
         \forgetall
         \blank[\v!disable]}

\protected\def\pack_framed_stop_content_indeed
         {\removelastskip
       \egroup
       \vskip\scratchbottomoffset
     \egroup
     \kern\scratchrightoffset
   \egroup
   \ifcstok{\framedcontentparameter\c!width}\v!fit
     \letframedcontentparameter\c!width\v!fixed% no shapebox
   \fi
   \ifinsidefloat
     \donefalse
   \orelse\ifcstok{\framedcontentparameter\c!linecorrection}\v!yes
     \donetrue
   \else
     \donefalse
   \fi
   % plaats ?
   \ifdone\startlinecorrection\fi
   \framedcontentparameter\c!left % new
   \inheritedframedcontentframed{\box\b_framed_normal}% hm
   \framedcontentparameter\c!right % new
   \ifdone\stoplinecorrection\fi
   \egroup}

% A shared setting.

\setuplinewidth
  [\v!medium]

%D A Goodie:

\defineframed
  [\v!unframed]
  [\c!frame=\v!off,
   \c!rulethickness=\zeropoint,
   \c!foregroundstyle=\framedparameter\c!style,
   \c!foregroundcolor=\framedparameter\c!color]

%D Bonus (as defined in \type {pack-rul.lua}):
%D
%D \starttyping
%D \setbox\scratchbox\vbox{a\par aa\par aaa\par}
%D \the\dimexpr\themaxboxwidth\scratchbox\relax
%D \stoptyping

% \let\themaxboxwidth % defined at the lua end

%D Fast enough for most cases (we could do a massive \type {\ifhastok} instead):

\permanent\protected\def\doifelseframed#1%
         {\ifcstok{#1\c!frame      }\v!on\expandafter\firstoftwoarguments
   \orelse\ifcstok{#1\c!topframe   }\v!on\expandafter\firstoftwoarguments
   \orelse\ifcstok{#1\c!bottomframe}\v!on\expandafter\firstoftwoarguments
   \orelse\ifcstok{#1\c!leftframe  }\v!on\expandafter\firstoftwoarguments
   \orelse\ifcstok{#1\c!rightframe }\v!on\expandafter\firstoftwoarguments
   \orelse\ifempty{#1\c!background }\expandafter\secondoftwoarguments
     \else                          \expandafter\firstoftwoarguments\fi}

\protect \endinput

