%D \module
%D   [       file=spac-ver,
%D        version=2009.10.16, % 1997.03.31, was core-spa.tex
%D          title=\CONTEXT\ Spacing Macros,
%D       subtitle=Vertical,
%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.

% perfect test for - , end of line {\hsize{\hsize-4cm}\samplefile{tufte}}

\writestatus{loading}{ConTeXt Spacing Macros / Vertical}

\unprotect

%newgluespec \bodyfontlineheight        % why a skip
\newdimension\bodyfontlineheight        % why a skip
\newdimen    \bodyfontstrutheight
\newdimen    \bodyfontstrutdepth

%newgluespec \globalbodyfontlineheight  % why a skip
\newdimension\globalbodyfontlineheight  % why a skip
\newdimen    \globalbodyfontstrutheight
\newdimen    \globalbodyfontstrutdepth

\newgluespec \s_spac_vspacing_predefined
\newdimension\d_spac_overlay
\newdimension\d_spac_total
\newinteger  \c_spac_vspacing_ignore_parskip

\newconditional \c_spac_delay

% \enableexperiments[vspacing.delay]

% \overloaded\let\strutht  \undefined \newdimen\strutht   % already defined
% \overloaded\let\strutdp  \undefined \newdimen\strutdp
% \overloaded\let\struthtdp\undefined \newdimen\struthtdp

\registerctxluafile{spac-ver}{autosuffix}

% todo: use usernodes ?

% todo: itemize : intro ... only when there is one or two lines preceding and then
% keep these together i.e. \blank[intro]

% Isn't it about time to get rid of topskip i.e. make it equivalent to
% \openstrutheight so that we can remove delta code.
%
% There might be more namespace protection.

%D There are two ways to influence the interline spacing. The most general and often
%D most consistent way is using
%D
%D \showsetup{setupinterlinespace}
%D
%D For instance
%D
%D \starttyping
%D \setupinterlinespace[line=2.8ex]
%D \stoptyping
%D
%D This setting adapts itself to the bodyfontsize, while for instance saying
%D
%D \starttyping
%D \setupinterlinespace[line=12pt]
%D \stoptyping
%D
%D sets things fixed for all sizes, which is definitely not what we want. Therefore
%D one can also say:
%D
%D \starttyping
%D \definebodyfontenvironment[9pt][interlinespace=11pt]
%D \stoptyping
%D
%D One can still use \type {\setupinterlinespace} (without arguments) to set the
%D interline space according to the current font, e.g. a \type {\bfa}.

% will be cleaned up but it will stay messy because we accept so
% many variants

\newif\iflocalinterlinespace

\newgluespec\s_spac_vspacing_temp \s_spac_vspacing_temp\bigskipamount

\mutable\def\skipfactor    {.75}
\mutable\def\skipgluefactor{.25}

\permanent\def\normalskipamount
  {\openlineheight
     \ifgridlinesnapping
       % fixed
     \orelse\ifgridsnapping
       % fixed
     \orelse\ifconditional\c_spac_whitespace_flexible
       \s!plus \skipgluefactor\openlineheight
       \s!minus\skipgluefactor\openlineheight
     \fi
   \relax}

\ifdefined\bodyfontinterlinespace \else
  \lettonothing\bodyfontinterlinespace
\fi

\lettonothing\m_spac_normallineheight

\permanent\protected\def\presetnormallineheight % each bodyfont
  {\edef\normallineheight{\interlinespaceparameter\c!line}%
   \iflocalinterlinespace \else
     \edef\m_spac_normallineheight{\bodyfontinterlinespace}%
     \ifempty\m_spac_normallineheight
     \orelse\ifchkdimension\m_spac_normallineheight\or
       \let\normallineheight\m_spac_normallineheight
     \else
       \edef\normallineheight{\todimension\m_spac_normallineheight\dimexpr\fontbody\relax}%
     \fi
   \fi}

\newconditional\c_space_topskip_by_line
\newconditional\c_space_maxdepth_by_line

\permanent\protected\def\setupspecifiedinterlinespace[#S#1]%
  {\setupcurrentinterlinespace[#1]%
   \spac_linespacing_setup_specified_interline_space}

\def\spac_linespacing_setup_specified_interline_space
  {\edef\strutheightfactor    {\interlinespaceparameter\c!height   }%
   \edef\strutdepthfactor     {\interlinespaceparameter\c!depth    }%
   \edef\minimumstrutheight   {\interlinespaceparameter\c!minheight}%
   \edef\minimumstrutdepth    {\interlinespaceparameter\c!mindepth }%
   \edef\minimumlinedistance  {\interlinespaceparameter\c!distance }%
   \edef\normallineheight     {\interlinespaceparameter\c!line     }%
   \edef\topskipfactor        {\interlinespaceparameter\c!top      }%
   \edef\ntopskipfactor       {\interlinespaceparameter\c!ntop     }%
   \edef\maxdepthfactor       {\interlinespaceparameter\c!bottom   }%
   \edef\m_spac_vertical_baseline_stretch_factor{\interlinespaceparameter\c!stretch}%
   \edef\m_spac_vertical_baseline_shrink_factor {\interlinespaceparameter\c!shrink }%
   % often topskip does more bad than good, so:
   \ifx\topskipfactor\v!height
     \let\topskipfactor\strutheightfactor
     \c_space_topskip_by_line\conditionaltrue
   \else
     \c_space_topskip_by_line\conditionalfalse
   \fi
   \ifx\maxdepthfactor\v!depth
     \let\maxdepthfactor\strutdepthfactor
     \c_space_maxdepth_by_line\conditionalfalse
   \else
     \c_space_maxdepth_by_line\conditionaltrue
   \fi
   \updatelinesnapping
   \setfontparameters % redundant, can be \setstrut, test first
   \updateraggedskips}

\ifdefined\updatelinesnapping \else \lettonothing\updatelinesnapping \fi

\installcorenamespace{interlinespacerelative}

\aliased\let\setrelativeinterlinespace\relax % used elsewhere

\mutable\lettonothing\currentrelativeinterlinespace

\defcsname\??interlinespacerelative\v!on   \endcsname{\oninterlineskip}
\defcsname\??interlinespacerelative\v!off  \endcsname{\offinterlineskip}
\defcsname\??interlinespacerelative\v!reset\endcsname{\enforced\lettonothing\currentrelativeinterlinespace
                                                      \enforced\let\setrelativeinterlinespace\relax
                                                      \setfontparameters}
\defcsname\??interlinespacerelative\v!auto \endcsname{\enforced\let\setrelativeinterlinespace\spac_linespacing_set_relative_interlinespace}

\def\spac_linespacing_set_specified_relative_interlinespace#1% fragile?
  {\doifelsedimenstring{#1}%
     {\setupspecifiedinterlinespace[\c!line=#1]}%
     {\assignvalue{#1}\currentrelativeinterlinespace{1.00}{1.25}{1.50}%
      \spacing\currentrelativeinterlinespace}}

\permanent\protected\def\setuprelativeinterlinespace[#1]%
  {\processcommalist[#1]\spac_linespacing_setup_relative_interlinespace}

\def\spac_linespacing_setup_relative_interlinespace#1%
  {\ifcsname\??interlinespacerelative#1\endcsname
     \lastnamedcs
   \else
     \spac_linespacing_set_specified_relative_interlinespace{#1}%
   \fi}

\permanent\protected\def\spac_linespacing_set_relative_interlinespace
  {\ifempty\currentrelativeinterlinespace\else
     \spacing\currentrelativeinterlinespace
   \fi}

\lettonothing\m_spac_interlinespace

\protected\def\spac_linespacing_setup_use
  {\ifcsname\namedinterlinespacehash\m_spac_interlinespace\s!parent\endcsname
     \let\currentinterlinespace\m_spac_interlinespace
     \spac_linespacing_setup_specified_interline_space
 % \else
     % we only support named interlinespaces
   \fi}

\permanent\protected\def\useinterlinespaceparameter#1% see footnotes
  {\edef\m_spac_interlinespace{#1\c!interlinespace}%
   \ifempty\m_spac_interlinespace \else
     \spac_linespacing_setup_use
   \fi}

\newtoks\everysetupglobalinterlinespace
\newtoks\everysetuplocalinterlinespace

\newconditional\interlinespaceisset

\installcorenamespace{interlinespace}

\installcommandhandler \??interlinespace {interlinespace} \??interlinespace

\installmacrostack\currentinterlinespace

\permanent\overloaded\tolerant\protected\def\setupinterlinespace[#S#1]%
  {\ifarguments
     \interlinespaceisset\conditionaltrue
     \spac_linespacing_synchronize_local
   \else
     \interlinespaceisset\conditionaltrue
     \ifcsname\namedinterlinespacehash{#1}\s!parent\endcsname
        \cdef\currentinterlinespace{#1}%
        \spac_linespacing_setup_specified_interline_space
       %\dosetupspecifiedinterlinespaceindeed
     \else
       \spac_linespacing_setup_specified_or_relative[#1]%
     \fi
   \fi}

\def\spac_linespacing_setup_specified_or_relative[#S#1]%
  {\doifelseassignment{#1}\setupspecifiedinterlinespace\setuprelativeinterlinespace[#1]%
   \expandtoks\iflocalinterlinespace\everysetuplocalinterlinespace\else\everysetupglobalinterlinespace\fi}

\def\spac_linespacing_synchronize_local % adapts to the font
  {\localinterlinespacetrue
   \setfontparameters
   \updateraggedskips % funny one here
   \expandtoks\everysetuplocalinterlinespace
   \localinterlinespacefalse}

\lettonothing\p_spac_checked_interlinespace

\permanent\protected\def\dosetupcheckedinterlinespace#1% often a chain
  {\edef\p_spac_checked_interlinespace{#1}%
   \ifempty\p_spac_checked_interlinespace
     \spac_linespacing_synchronize_local
   \orelse\ifcsname\namedinterlinespacehash\p_spac_checked_interlinespace\s!parent\endcsname % we could have a \s!check
     \push_macro_currentinterlinespace
     \let\currentinterlinespace\p_spac_checked_interlinespace
     \spac_linespacing_setup_specified_interline_space % \dosetupspecifiedinterlinespaceindeed
     \iflocalinterlinespace
       \expandtoks\everysetuplocalinterlinespace
     \else
       \localinterlinespacetrue
       \expandtoks\everysetuplocalinterlinespace
       \localinterlinespacefalse
     \fi
     \pop_macro_currentinterlinespace
   \else
     \expanded{\noexpand\doifelseassignment{\p_spac_checked_interlinespace}%
       \setupspecifiedinterlinespace\setuprelativeinterlinespace[\p_spac_checked_interlinespace]}%
     \iflocalinterlinespace
       \expandtoks\everysetuplocalinterlinespace
     \else
       \localinterlinespacetrue
       \expandtoks\everysetuplocalinterlinespace
       \localinterlinespacefalse
     \fi
   \fi}

\permanent\protected\def\setuplocalinterlinespace[#S#1]%
  {\localinterlinespacetrue
   \push_macro_currentinterlinespace
   \setupinterlinespace[#1]%
   \pop_macro_currentinterlinespace
   \localinterlinespacefalse}

\aliased\let\switchtointerlinespace\setuplocalinterlinespace

%D Helpers

\newgluespec \s_spac_lastskip
\newdimension\d_spac_prevdepth
\newinteger  \c_spac_spacefactor
%newdimension\d_spac_prevcontent % set by lua (no longer it seems)

% \permanent\overloaded\protected\def\removelastskip
%   {\ifvmode\ifzeropt\lastskip\else\vskip-\lastskip\fi\fi}

\permanent\def\doifoutervmode % was only used in \presetindentation
  {\unless\ifvmode
     \expandafter\gobbleoneargument
   \orelse\ifinner
     \expandafter\gobbleoneargument
   \else
     \expandafter\firstofoneargument
   \fi}

\permanent\protected\def\dosomebreak#1% should be replaced by something \blank
  {\doifoutervmode
     {\s_spac_lastskip\lastskip
      \removelastskip
      #1\relax
      \ifzeropt\s_spac_lastskip
        % avoid interference with footnotes
      \else
        \vskip\s_spac_lastskip
      \fi}}

\permanent\protected\def\packed
  {\nointerlineskip}

\permanent\protected\def\godown[#1]%
  {\relax
   \ifhmode\endgraf\fi
   \ifvmode\nointerlineskip\vskip#1\relax\fi}

\permanent\protected\def\smallskip{\vskip\smallskipamount}
\permanent\protected\def\medskip  {\vskip\medskipamount}
\permanent\protected\def\bigskip  {\vskip\bigskipamount}

\permanent\protected\def\smallbreak
  {\par
   \ifvmode\ifdim\lastskip<\smallskipamount
     \removelastskip
     \penalty-\plusfifty
     \smallskip
   \fi\fi}

\permanent\protected\def\medbreak
  {\par
   \ifvmode\ifdim\lastskip<\medskipamount
     \removelastskip
     \penalty-\plusonehundred
     \medskip
   \fi\fi}

\permanent\protected\def\bigbreak
  {\par
   \ifvmode\ifdim\lastskip<\bigskipamount
     \removelastskip
     \penalty-\plustwohundred
     \bigskip
   \fi\fi}

\permanent\protected\def\break      {\penalty-\plustenthousand}  % can be hmode or vmode
\permanent\protected\def\nobreak    {\penalty \plustenthousand}
\permanent\protected\def\allowbreak {\penalty \zerocount}

\permanent\protected\def\vbreak     {\vpenalty-\plustenthousand} % ensures vmode
\permanent\protected\def\novbreak   {\vpenalty \plustenthousand}
\permanent\protected\def\allowvbreak{\vpenalty \zerocount}

\permanent\protected\def\hbreak     {\hpenalty-\plustenthousand} % ensures vmode
\permanent\protected\def\nohbreak   {\hpenalty \plustenthousand}
\permanent\protected\def\allowhbreak{\hpenalty \zerocount}

\permanent\protected\def\goodbreak {\par\ifvmode\penalty-\plusfivehundred\relax\fi}       % forces vmode
\permanent\protected\def\filbreak  {\par\ifvmode\vfil\penalty-\plustwohundred\vfilneg\fi} % forces vmode

%D Made slightly more readable:

\permanent\protected\def\vglue  {\afterassignment\spac_helpers_vglue_indeed\s_spac_lastskip=}
\permanent\protected\def\hglue  {\afterassignment\spac_helpers_hglue_indeed\s_spac_lastskip=}
\permanent\protected\def\topglue{\par\ifvmode\nointerlineskip\vglue-\topskip\vglue\fi}

\def\spac_helpers_vglue_indeed
  {\par
   \ifvmode
     \d_spac_prevdepth\prevdepth
     \hrule\s!height\zeropoint
     \nobreak
     \vskip\s_spac_lastskip
     \prevdepth\d_spac_prevdepth
   \fi}

\def\spac_helpers_hglue_indeed
  {\dontleavehmode
   \c_spac_spacefactor\spacefactor
   \vrule\s!width\zeropoint
   \nobreak
   \hskip\s_spac_lastskip
   \spacefactor\c_spac_spacefactor}

%D We adapt plain's \type {\removelastskip} a bit:

\pushoverloadmode

\permanent\overloaded\protected\def\removelastskip % also in supp-box
  {\ifvmode\ifzeropt\lastskip\else\vskip-\lastskip\fi\fi}

\popoverloadmode

% The whitespace handler. We could cache settings but normally there are not
% that many in a set.

\installcorenamespace{whitespacemethod}

\newgluespec   \s_spac_whitespace_parskip
\newconditional\c_spac_whitespace_flexible  \c_spac_whitespace_flexible\conditionaltrue

%def\v_spac_whitespace_current{\zeropoint}
\let\v_spac_whitespace_current\v!none

\permanent\tolerant\protected\def\setupwhitespace[#1]%
  {\ifarguments
     \spac_whitespace_setup_nop
   \orelse\ifempty{#1}%
     \spac_whitespace_setup_nop
   \else
     \edef\v_spac_whitespace_current{#1}%
     \spac_whitespace_setup
   \fi}

\permanent\protected\def\spac_whitespace_setup_nop
  {\ifx\v_spac_whitespace_current\v!none\else
     \spac_whitespace_setup
   \fi}

\aliased\let\synchronizewhitespace\spac_whitespace_setup_nop

\def\spac_whitespace_setup % quick test for no list
  {\ifcsname\??whitespacemethod\v_spac_whitespace_current\endcsname
     \lastnamedcs
   \else
     \expandafter\processcommalist\expandafter[\v_spac_whitespace_current]\spac_whitespace_setup_method % can be raw
   \fi\relax
   \ifgridlinesnapping
     \spac_whitespace_setup_grid
   \orelse\ifgridsnapping
     \spac_whitespace_setup_grid
   \else
     \spac_whitespace_setup_normal
   \fi
   \parskip\s_spac_whitespace_parskip}

\def\spac_whitespace_setup_normal
  {\ifconditional\c_spac_whitespace_flexible \else
     \s_spac_whitespace_parskip\plusone\s_spac_whitespace_parskip
   \fi}

\def\spac_whitespace_setup_grid
  {\c_spac_whitespace_flexible\conditionalfalse
   \ifdim\s_spac_whitespace_parskip>\zeropoint
     \s_spac_whitespace_parskip\baselineskip
   \fi}

\permanent\protected\def\installwhitespacemethod#1#2%
  {\defcsname\??whitespacemethod#1\endcsname{#2}}

\installwhitespacemethod \v!fix         {}
\installwhitespacemethod \v!fixed       {\c_spac_whitespace_flexible\conditionalfalse}
\installwhitespacemethod \v!flexible    {\c_spac_whitespace_flexible\conditionaltrue}

\installwhitespacemethod \v!line        {\s_spac_whitespace_parskip   \baselineskip}
\installwhitespacemethod \v!halfline    {\s_spac_whitespace_parskip .5\baselineskip}
\installwhitespacemethod \v!quarterline {\s_spac_whitespace_parskip.25\baselineskip}
\installwhitespacemethod \v!none        {\s_spac_whitespace_parskip   \zeroskip}
\installwhitespacemethod \v!big         {\s_spac_whitespace_parskip   \bigskipamount}
\installwhitespacemethod \v!medium      {\s_spac_whitespace_parskip   \medskipamount}
\installwhitespacemethod \v!small       {\s_spac_whitespace_parskip   \smallskipamount}

\installwhitespacemethod \s!default     {\spac_whitespace_setup_nop} % also covers none

\def\spac_whitespace_setup_method#1%
  {\ifcsname\??whitespacemethod#1\endcsname
     \lastnamedcs
   \else
     \s_spac_whitespace_parskip#1%
   \fi\relax}

\permanent\protected\def\forgetparskip
  {\s_spac_whitespace_parskip\zeroskip
   \parskip\zeroskip
   \let\v_spac_whitespace_current\v!none}

\appendtoks
    \forgetparskip
\to \everyforgetall

% \installwhitespacemethod \s!unknown  {\s_spac_whitespace_parskip\commalistelement\relax}
%
% \def\spac_whitespace_setup_method#1%
%   {\csname\??whitespacemethod\ifcsname\??whitespacemethod#1\endcsname#1\else\s!unknown\endcsname\relax}

\permanent\protected\def\nowhitespace{\directcheckedvspacing\v!nowhite} % {\vspacing[\v!nowhite]}
\permanent\protected\def\whitespace  {\directcheckedvspacing\v!white}   % {\vspacing[\v!white]}

\setupwhitespace
  [\v!none]

% Packed:

% todo: when packed blocks blank, we need to enable forced

\newconditional\c_spac_packed_blank \c_spac_packed_blank\conditionaltrue
\newinteger    \c_spac_packed_level

\permanent\tolerant\protected\def\startpacked[#1]%
  {\global\advanceby\c_spac_packed_level\plusone
   \par
   \ifnum\c_spac_packed_level=\plusone \ifvmode
     \begingroup
     \whitespace % not combined
     \directcheckedvspacing\v!disable % \blank[\v!disable]% or \inhibitblank
     \ifcstok{#1}\v!blank
       \c_spac_packed_blank\conditionaltrue
     \else
       \c_spac_packed_blank\conditionalfalse
     \fi
     \setupwhitespace[\v!none]% or \forgetparskip
   \fi \fi}

\permanent\protected\def\stoppacked
  {\par
   \ifnum\c_spac_packed_level=\plusone \ifvmode
     \endgroup
   \fi \fi
   \global\advanceby\c_spac_packed_level\minusone}

\permanent\protected\def\startunpacked
  {\directdefaultvspacing % \blank
   \begingroup}

\permanent\protected\def\stopunpacked
  {\endgroup
   \directdefaultvspacing}% \blank}

% \prevdepth crosses pageboundaries!
%
% todo: a version that works ok inside a box

% global : outer hsize + keep skips
% local  : inner hsize + reset skips

\installcorenamespace{linesaround}

\let\spac_lines_vbox\vbox

\installtextracker
  {linecorrection.boxes}
  {\enforced\let\spac_lines_vbox\ruledvbox}
  {\enforced\let\spac_lines_vbox\vbox}

\let\v_spac_lines_around_action_set\relax

\lettonothing\spac_lines_action_around_before
\lettonothing\spac_lines_action_around_after

\newconstant\c_spac_lines_correction_mode

\installcorenamespace{linecorrection}

\installsetuponlycommandhandler \??linecorrection {linecorrection}

\permanent\tolerant\protected\def\startlinecorrection     [#S#1]{\spac_lines_start_correction\plusone{#1}}
\permanent\tolerant\protected\def\startlocallinecorrection[#S#1]{\spac_lines_start_correction\plustwo{#1}}

\permanent\protected\def\spac_lines_start_correction_box
  {\setbox\scratchbox\spac_lines_vbox\bgroup
     \ifcase\c_spac_lines_correction_mode
       % nothing
     \or
       % global
     \or
       % local
       \setlocalhsize
       \hsize\localhsize
       \forgetbothskips
     \fi
     \ignorespaces}

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

\permanent\protected\def\spac_lines_start_correction#1#2%
  {\endgraf
   \begingroup
   \c_spac_lines_correction_mode#1%
   % a bit of history
   \ifhastok={#2}%
     \setupcurrentlinecorrection[#2]%
     \def\spac_lines_action_around_before{\linecorrectionparameter\c!before}%
     \def\spac_lines_action_around_after {\linecorrectionparameter\c!after}%
   \orelse\ifempty{#2}%
     \def\spac_lines_action_around_before{\linecorrectionparameter\c!before}%
     \def\spac_lines_action_around_after {\linecorrectionparameter\c!after}%
   \orelse\ifcstok{#2}\v!blank
     \def\spac_lines_action_around_before{\blank}%
     \let\spac_lines_action_around_after \spac_lines_action_around_before
   \else
     \def\spac_lines_action_around_before{\blank[#2]}%
     \let\spac_lines_action_around_after \spac_lines_action_around_before
   \fi
   \ifgridlinesnapping
     \spac_lines_start_correction_snapping
   \orelse\ifgridsnapping
     \spac_lines_start_correction_ongrid
   \else
     \spac_lines_start_correction_normal
   \fi}

\permanent\protected\def\spac_lines_stop_correction
  {\ifgridlinesnapping
     \spac_lines_stop_correction_snapping
   \orelse\ifgridsnapping
     \spac_lines_stop_correction_ongrid
   \else
     \spac_lines_stop_correction_normal
   \fi
   \endgroup}

\permanent\protected\def\spac_lines_start_correction_normal
  {\spac_lines_action_around_before
   \d_spac_prevdepth\prevdepth
   \spac_lines_initialize_corrections
   \offbaselinecorrection % ???
   \spac_lines_start_correction_box}

\newconstant\linecorrectionmode

% \linecorrectionmode\plusone % better for end of column and depth alignment

% \permanent\protected\def\spac_trigger_page_builder
%   {\pageboundary\zerocount\plustenthousand} % becomes a penalty (after triggering the callback) (experimental!)

\permanent\protected\def\spac_trigger_page_builder
  {\ifconditional\c_spac_delay
     \checkspacingbeforepar
   \else
     \pageboundary\zerocount\plustenthousand
   \fi}

\permanent\protected\def\spac_lines_stop_correction_normal
  {\spac_lines_stop_correction_box
   \ifnum\linecorrectionmode=\plusone
     \forcestrutdepth
     \iftrue
       \penalty\zerocount % works too
     \else
       \spac_trigger_page_builder
     \fi
   \else
     \directcheckedvspacing\v!nowhite % \blank[\v!nowhite]%
     \ifdim\parskip>\zeropoint
        % too fuzzy otherwise
     \orelse\ifdim\d_spac_prevdepth<\maxdimen
       \unless\ifdim\d_spac_prevdepth<\zeropoint
         \ifdim\d_spac_prevdepth<\strutdp \relax
           \pushlastnode
           \ifdim\d_spac_prevdepth>\zeropoint
             \kern-\d_spac_prevdepth
           \fi
           \kern\strutdp
           \prevdepth\strutdp
           \poplastnode
         \fi
       \fi
     \fi
   \fi
   \ifdim\pagegoal<\maxdimen
   % \blank[\v!white,\the\d_spac_lines_correction_before]% \blank[\v!white]\dotopbaselinecorrection
     \directcheckedvspacing{\v!white,\the\d_spac_lines_correction_before}% \blank[\v!white]\dotopbaselinecorrection
   \fi
   \nointerlineskip % relatively new
   \noindent % not \dontleavehmode !
   \ifcase\c_spac_lines_correction_mode
     % nothing
   \or
     % global
     \hskip-\leftskip % more tricky would be hangindent so we ignore that one
   \or
     % local
   \fi
   \box\scratchbox
   \endgraf
   %
   % eventually i'll get it right ... (i also need to check all whitespace code elsewhere)
   %
 % \blank[\the\d_spac_lines_correction_after]% \dobotbaselinecorrection
 % \directcheckedvspacing{\the\d_spac_lines_correction_after}% \dobotbaselinecorrection
   \directcheckedvspacing{\v!white,\the\d_spac_lines_correction_after}% \dobotbaselinecorrection
 % \allowbreak % new, otherwise problems when many in a row
   \prevdepth\strutdp
   \spac_lines_action_around_after}

% this needs checking: a double before .. use lmtx tricks

\permanent\protected\def\spac_lines_start_correction_ongrid
  {%spac_lines_action_around_before
   \d_spac_prevdepth\prevdepth
   \spac_lines_initialize_corrections
   \offbaselinecorrection % ???
   \spac_lines_start_correction_box}

\permanent\protected\def\spac_lines_stop_correction_ongrid
  {\spac_lines_stop_correction_box
   \directcheckedvspacing\v!white % \blank[\v!white]%
   \spac_lines_action_around_before
   \snaptogrid\hpack{\box\scratchbox}%
   \directcheckedvspacing\v!white
   \spac_lines_action_around_after}

\permanent\protected\def\spac_lines_start_correction_snapping
  {\spac_lines_start_correction_box
   \linesnapping\zerocount}

\permanent\protected\def\spac_lines_stop_correction_snapping
  {\spac_lines_stop_correction_box
   \directcheckedvspacing\v!white % \blank[\v!white]%
   \spac_lines_action_around_before
   \scratchdimen\htdp\scratchbox
   \boxsnapping\scratchbox\linesnappingtwo
   \boxyoffset \scratchbox {-(\htdp\scratchbox-\scratchdimen):\plustwo}%
   % we assume lineskip 0pt and using \nointerlineskip will fail us here
  %\nointerlineskip
   \box\scratchbox
  %\nointerlineskip
   \directcheckedvspacing\v!white
   \spac_lines_action_around_after}

\aliased\let\stoplinecorrection     \spac_lines_stop_correction
\aliased\let\stoplocallinecorrection\spac_lines_stop_correction

% todo:

\permanent\protected\def\correctwhitespace
  {\dowithnextboxcs\correctwhitespacefinish\vbox}

\permanent\protected\def\correctwhitespacefinish
  {\startbaselinecorrection
   \flushnextbox
   \stopbaselinecorrection}

\permanent\protected\def\verticalstrut  {\vpack{\hsize\zeropoint\forgetall\strut}}
\permanent\protected\def\horizontalstrut{\hpack                          {\strut}}

%D Here follow some presets related to interline spacing and therefore also struts.
%D The values 2.8, 0.07, 0.72 and 0.28 originate in \INRSTEX, a package that we used
%D a while after we decided that \LATEX\ was not flexible enough. After that
%D \CONTEXT\ evolved, from some wrapper code around (old) \LATEX\ (on a floppy
%D disk), to using modules from \INRSTEX\ (which also fit on a floppy) and finally
%D all written from scratch. I simply didn't understand all that \TEX\ code at that
%D time, and it was easier to figure it out myself. But \unknown\ some settings
%D stayed, as the height|/|depth ratios, and they never proved to be bad ones! The
%D same is true for the font size relations.

%D \starttabulate
%D \NC  \type {\lineheight}        \NC the height of a line \NC \NR
%D \NC  \type {\spacing{number}}   \NC adapting the interline space \NC \NR
%D \NC  \type {\normalbaselines}   \NC initialize the interline spacing \NC \NR
%D \NC  \type {\setstrut}          \NC initialize \type {\strut} \NC \NR
%D \NC  \type {\setnostrut}        \NC disable the \type {\strut}, \type {\endstrut}, \type {\begstrut} \NC \NR
%D \NC  \type {\setteststrut}      \NC initialize the visual \type {\strut} \NC \NR
%D \NC  \type {\resetteststrut}    \NC disable the visual \type {\strut} \NC \NR
%D \NC  \type {\setfontparameters} \NC synchronize parameters with foints \NC \NR
%D \stoptabulate
%D
%D \unknown\ and many more (this is a decades old list).
%D
%D The lineheight is the sum of the height and depth of \type {strut}, which is
%D an invisible blob that can be used to enforce the proper dimensions.
%D
%D Such a blob, when placed at the beginning of a paragraph can have side effects
%D that can be prevented with \type {\dontleavehmode}. Never use \type
%D {\leavevmode}!

\newdimension\strutdimen        % not used
\newdimension\lineheight
\newdimension\openlineheight
\newdimension\openstrutheight
\newdimension\openstrutdepth
\newdimension\topskipgap

\newdimension\strutheight
\newdimension\strutdepth
\newdimension\struttotal
\newdimension\strutwidth

\let\m_spac_vertical_baseline_stretch_factor \zerocount
\let\m_spac_vertical_baseline_shrink_factor  \zerocount

\mutable\def\strutheightfactor   {.72}
\mutable\def\strutdepthfactor    {.28}
\mutable\def\baselinefactor      {2.8}
\mutable\def\topskipfactor       {1.0}
\mutable\def\maxdepthfactor      {0.5}
\mutable\let\ntopskipfactor      \minusone

\mutable\def\minimumstrutheight  {\zeropoint}
\mutable\def\minimumstrutdepth   {\zeropoint}

\mutable\def\normallineheight    {\baselinefactor\exheight}
\mutable\def\minimumlinedistance {\lineskip}

\mutable\let\spacingfactor        \plusone

\mutable\def\systemtopskipfactor {\topskipfactor}
\mutable\def\systemmaxdepthfactor{\maxdepthfactor}

\ifdefined\globalbodyfontsize \else
    \newdimension\globalbodyfontsize
    \globalbodyfontsize=12pt
\fi

\ifdefined\normalizedbodyfontsize \else
    \def\normalizedbodyfontsize{12pt}
\fi

\permanent\protected\def\topskipcorrection
  {\simpletopskipcorrection
   \vskip-\struttotal
   \verticalstrut}

\permanent\protected\def\simpletopskipcorrection
  {\ifdim\topskip>\openstrutheight
     % == \vskip\topskipgap
     \vskip\topskip
     \vskip-\openstrutheight
   \fi}

\permanent\protected\def\settopskip % the extra test is needed for the lbr family
  {\topskip
     \ifgridlinesnapping
       \zeroskip
       \initialpageskip\zeroskip
     \orelse\ifgridsnapping
       \zeroskip
       \initialpageskip\zeroskip
     \else
       \systemtopskipfactor
       \ifconditional\c_space_topskip_by_line
         \openlineheight
       \else
         \globalbodyfontsize
       \fi
       \relax
       \initialpageskip\zeropoint
         \ifcase\bottomraggednessmode % ragged bottom
           \s!plus \ntopskipfactor\openlineheight
         \fi
       \relax
   \fi
   \topskipgap\topskip
   \advanceby\topskipgap -\openstrutheight\relax
   \ifdim\minimumstrutheight>\zeropoint
     \ifdim\topskip<\minimumstrutheight
       \topskip\minimumstrutheight\relax
     \fi
   \else
     \ifdim\topskip<\strutheightfactor\openlineheight
       \topskip\strutheightfactor\openlineheight\relax
     \fi
   \fi}

% \permanent\protected\def\setmaxdepth
%   {\maxdepth\systemmaxdepthfactor\globalbodyfontsize}

\permanent\protected\def\setmaxdepth
  {\maxdepth\systemmaxdepthfactor\globalbodyfontsize
   \ifdim\minimumstrutdepth>\zeropoint
     \ifdim\maxdepth<\minimumstrutdepth
       \maxdepth\minimumstrutdepth\relax
     \fi
  \else
     \ifdim\maxdepth<\strutdepthfactor\openlineheight
       \maxdepth\strutdepthfactor\openlineheight\relax
     \fi
   \fi}

\newgluespec \usedbaselineskip  % These used to be \normal... but that isn't pretty
\newgluespec \usedlineskip      % in the token interface, so these few now have new
\newdimension\usedlineskiplimit % names. They are public but not really user commands.

\permanent\protected\def\normalbaselines
  {\baselineskip \usedbaselineskip
   \lineskip     \usedlineskip
   \lineskiplimit\usedlineskiplimit}

% \permanent\protected\def\flexiblebaselines
%   {\baselineskip  \usedbaselineskip
%    \lineskip     1\usedlineskip \s!plus 1\s!fill
%    \lineskiplimit \usedlineskiplimit}

\permanent\protected\def\flexiblebaselines
  {\baselineskip 1\usedbaselineskip \s!plus 1\s!fill
   \lineskip      \usedlineskip
   \lineskiplimit \usedlineskiplimit}

\permanent\protected\def\setnormalbaselines % used in overload
  {\ifdim\normallineheight>\zeropoint
     \lineheight\normallineheight
   \fi
   \openlineheight\spacingfactor\lineheight
   \openstrutheight \ifdim\minimumstrutheight>\zeropoint
     \minimumstrutheight % new
   \else
     \strutheightfactor\openlineheight
   \fi
   \openstrutdepth \ifdim\minimumstrutdepth>\zeropoint
     \minimumstrutdepth % new
   \else
     \strutdepthfactor \openlineheight
   \fi
   \ifdim{\minimumstrutdepth+\minimumstrutheight}>\zeropoint
     \openlineheight{\openstrutheight+\openstrutdepth}% new
   \fi
   \usedbaselineskip\openlineheight
     \ifgridlinesnapping
       % fixed
     \orelse\ifgridsnapping
       % fixed
     \else
       \s!plus \m_spac_vertical_baseline_stretch_factor\openlineheight
       \s!minus\m_spac_vertical_baseline_shrink_factor \openlineheight
     \fi
   \relax
   \usedlineskip
     \ifgridlinesnapping
       \zeropoint
     \orelse\ifgridsnapping
       \zeropoint
     \else
       \minimumlinedistance % \onepoint
     \fi
   \relax
   \usedlineskiplimit
     \zeropoint
   \relax
   \normalbaselines}

\permanent\protected\def\spacing#1% vertical
  {\ifgridsnapping
     \let\spacingfactor\plusone
   \orelse\ifgridsnapping
     \let\spacingfactor\plusone
   \else
     \permanent\edef\spacingfactor{#1}%
   \fi
   \edef\systemtopskipfactor {\thewithoutunit\dimexpr#1\dimexpr\topskipfactor \points}% {}
   \edef\systemmaxdepthfactor{\thewithoutunit\dimexpr#1\dimexpr\maxdepthfactor\points}% {}
   \setnormalbaselines
   \setstrut}

% \protected\def\forgetverticalstretch % \forgetspacing
%   {\spacing\plusone}

\permanent\protected\def\forgetverticalstretch
  {\let\spacingfactor       \plusone
   \let\systemtopskipfactor \topskipfactor
   \let\systemmaxdepthfactor\maxdepthfactor
   \setnormalbaselines
   \setstrut}

\appendtoks
    \forgetverticalstretch
\to \everyforgetall % needed in otr

%D Sometimes one needs to freeze the interlinespacing
%D
%D \starttyping
%D \rm \saveinterlinespace .... {\ss \restoreinterlinespace .... \endgraf}
%D \stoptyping
%D
%D This is no longer needed.

\aliased\let\restoreinterlinespace\relax

\permanent\protected\def\saveinterlinespace
  {\enforced\permanent\protected\edef\restoreinterlinespace
     {\lineheight         \the\lineheight
      \openstrutheight    \the\openstrutheight
      \openstrutdepth     \the\openstrutdepth
      \openlineheight     \the\openlineheight
      \usedbaselineskip   \the\usedbaselineskip
      \usedlineskip       \the\usedlineskip
      \usedlineskiplimit  \the\usedlineskiplimit
      \noexpand\def\noexpand\normallineheight{\todimension{\normallineheight}}%
      \noexpand\normalbaselines}}

%D This is the plain definition:
%D
%D \starttyping
%D \def\strut{\relax\ifmmode\copy\strutbox\else\unhcopy\strutbox\fi}
%D \stoptyping
%D
%D which could be:
%D
%D \starttyping
%D \def\strut{\relax\ifmmode\copy\else\unhcopy\fi\strutbox}
%D \stoptyping
%D
%D But we do things differently.

\newbox\b_strut_box
\newbox\b_strut_tmp

\permanent\protected\def\strutbox % not to be used but this is sort of an alias
  {\beginlocalcontrol
   \setbox\b_strut_tmp\hpack{%
     \normalsrule
        %s!height\strutht
        %s!depth \strutdp
        \s!pair  \strutht \strutdp
   }
   \endlocalcontrol
   \b_strut_tmp}

%D The double \type {\hbox} construction enables us to backtrack boxes.

% \overloaded\let\strutht  \undefined \newdimension\strutht    % see above
% \overloaded\let\strutdp  \undefined \newdimension\strutdp
% \overloaded\let\struthtdp\undefined \newdimension\struthtdp

\permanent\protected\def\setstrut
  {\ifgridlinesnapping
     \setstrutgridyes
   \orelse\ifgridsnapping
     \setstrutgridyes
   \else
     \setstrutgridnop
   \fi}

\permanent\protected\def\setstrutgridyes
  {\strutht\spacingfactor\dimexpr
     \ifdim\minimumstrutheight>\zeropoint
       \minimumstrutheight
     \else
       \strutheightfactor\dimexpr\normallineheight
     \fi
   \relax
   \strutdp\dimexpr
     \ifdim\minimumstrutdepth>\zeropoint
       \minimumstrutdepth
     \else
       \normallineheight-\strutht
     \fi
   \relax
   \dosetstrut}

\permanent\protected\def\setstrutgridnop
  {\strutht\spacingfactor\dimexpr
     \ifdim\minimumstrutheight>\zeropoint
       \minimumstrutheight
     \else
       \strutheightfactor\dimexpr\normallineheight\relax
     \fi
   \relax
   \strutdp\spacingfactor\dimexpr
     \ifdim\minimumstrutdepth>\zeropoint
       \minimumstrutdepth
     \else
       \strutdepthfactor\dimexpr\normallineheight\relax
     \fi
   \relax
   \dosetstrut}

\permanent\protected\def\setcharstrut#1%
  {\setbox\b_strut_box\hbox{#1}% no \hpack, in case we have smallcaps
   \strutht\ht\b_strut_box
   \strutdp\dp\b_strut_box
   \dosetstrut}

\permanent\protected\def\settightstrut
  {\setcharstrut{(}}

\permanent\protected\def\setfontstrut
  {\setcharstrut{(gplQT}}

\permanent\protected\def\setcapstrut% could be M, but Q has descender
  {\setcharstrut{Q}}

%D Handy for math (used in mathml):

\permanent\protected\def\charhtstrut
  {\begingroup
   \setcharstrut{GJY}%
   \normalsrule
     %s!height\strutht
     %s!depth \zeropoint
     \s!pair  \strutht \zeropoint
   \endgroup}

\permanent\protected\def\chardpstrut
  {\begingroup
   \setcharstrut{gjy}%
   \normalsrule
     %s!height\zeropoint
     %s!depth \strutdp
     \s!pair  \zeropoint \strutdp
   \endgroup}

%D Because of all the callbacks in mkiv, we avoid unnecessary boxes ... maybe use an
%D attribute so that we can tag boxes that don't need a treatment; tests with using
%D an attribute so far have shown that it's slower because testing the attribute
%D takes time too.

\permanent\protected\def\dosetstrut
  {\enforced\let\strut\normalstrut
   \struthtdp{\strutht+\strutdp}%
   \ifabsnum\dimexpr\struthtdp-\lineheight\relax<\plustwo
     % compensate rounding error /- 1sp to avoid too many
     % 1sp baselineskips in for instance verbatim
     % \strutht\dimexpr\lineheight-\strutdp\relax
     % better:
     \strutdp{\lineheight-\strutht}%
     \struthtdp\lineheight
   \fi
   \strutheight\strutht
   \strutdepth \strutdp
   \struttotal \struthtdp}

\newconstant\c_strut_visual_mode

%D The dimen \type {\struttotal} holds the exact size of the strut; occasionally a
%D one scaled point difference can show up with the lineheight. This is more
%D efficient (less callbacks):

\permanent\protected\def\strut % still callbacks for \hbox{\strut}
  {\relax
   \dontleavehmode
   \normalsrule
      %s!height\strutht
      %s!depth \strutdp
      \s!pair  \strutht \strutdp
   \relax}

\aliased\let\normalstrut\strut

\permanent\protected\def\halfstrut
  {\relax
   \dontleavehmode
   \normalsrule
      %s!height.5\strutht
      %s!depth .5\strutdp
      \s!pair  .5\strutht .5\strutdp
   \relax}

\permanent\protected\def\quarterstrut
  {\relax
   \dontleavehmode
   \normalsrule
      %s!height.25\strutht
      %s!depth .25\strutdp
      \s!pair  .25\strutht .25\strutdp
   \relax}

\permanent\protected\def\depthstrut
  {\relax
   \dontleavehmode
   \normalsrule
      %s!height{\strutht-\struthtdp/\plustwo}%
      %s!depth \strutdp
      \s!pair  {\strutht-\struthtdp/\plustwo} \strutdp
   \relax}

\permanent\protected\def\halflinestrut
  {\relax
   \dontleavehmode
   \normalsrule
      %s!height{\strutht-.5\struthtdp}%
      %s!depth \strutdp
      \s!pair  {\strutht-.5\struthtdp} \strutdp
   \relax}

\permanent\protected\def\noheightstrut
  {\relax
   \dontleavehmode
   \normalsrule
      %s!height\zeropoint
      %s!depth \strutdp
      \s!pair  \zeropoint \strutdp
   \relax}

\permanent\protected\def\nodepthstrut
  {\relax
   \dontleavehmode
   \normalsrule
      %s!height\strutht
      %s!depth \zeropoint
      \s!pair  \strutht \zeropoint
   \relax}

%D Sometimes a capstrut comes in handy
%D
%D \starttabulate[|Tl|l|l|]
%D \NC yes          \NC normal strut               \NC {\showstruts\setupstrut[yes]\strut}  \NC \NR
%D \NC no           \NC no strut                   \NC {\showstruts\setupstrut[no]\strut}  \NC \NR
%D \NC kap          \NC a capital strut (i.e. Q)   \NC {\showstruts\setupstrut[cap]\strut} \NC \NR
%D \NC A B \unknown \NC a character strut (e.g. A) \NC {\showstruts\setupstrut[A]\strut}   \NC \NR
%D \NC              \NC a normal strut             \NC {\showstruts\setupstrut\strut}      \NC \NR
%D \stoptabulate
%D
%D Beware: using an unknown value results in char struts.

\installcorenamespace{struts}

\permanent\tolerant\protected\def\setupstrut[#1]%
  {\edef\m_strut{#1}%
   \ifcsname\??struts\m_strut\endcsname
     \lastnamedcs
   \else
     \setcharstrut\m_strut
   \fi}

\permanent\protected\def\synchronizestrut#1% no [] parsing, faster for internal
  {\edef\m_strut{#1}%
   \ifcsname\??struts\m_strut\endcsname
     \lastnamedcs
   \else
     \setcharstrut\m_strut
   \fi}

\permanent\protected\def\dosynchronizestrut#1% no [] parsing, faster for internal
  {\ifcsname\??struts#1\endcsname
     \lastnamedcs
   \else
     \setcharstrut{#1}%
   \fi}

\permanent\protected\def\showstruts % adapts .. is wrong
  {\showmakeup[strut]%
   \settestcrlf}

\aliased\let\showcolorstruts\showstruts

\mutable\def\autostrutfactor{1.1}

\permanent\protected\def\setautostrut
  {\begingroup
   \setbox\scratchbox\copy\b_strut_box
   \setstrut
   \ifdim\strutht>\autostrutfactor\ht\scratchbox
     \endgroup \setstrut
   \orelse\ifdim\strutdp>\autostrutfactor\dp\scratchbox
     \endgroup \setstrut
   \else
     \endgroup
   \fi}

\newbox\nostrutbox \setbox\nostrutbox\emptyhbox

\newtoks\everysetnostrut

\permanent\protected\def\setnostrut
  {\expandtoks\everysetnostrut}

\appendtoks
 %  \setbox\strutbox\copy\nostrutbox
    \enforced\lettonothing\strut
    \enforced\lettonothing\endstrut
    \enforced\lettonothing\begstrut
\to \everysetnostrut

%D When enabled, sigstruts will remove themselves if nothing goes inbetween. For
%D practical reasons we define some boundary characters here.

\permanent\protected\def\leftboundary {\protrusionboundary\plusone}
\permanent\protected\def\rightboundary{\protrusionboundary\plustwo}

\permanent\protected\def\signalcharacter % not the same as strut signals
  {\boundary\plusone
   \char\zerocount
   \boundary\plustwo}

\permanent\protected\def\signalglyph
  {\boundary \plusone
   \glyph \s!keepspacing \zerocount
   \boundary \plustwo}

%D \starttyping
%D $ \ifhmode H1\fi x \ifhmode H2\fi $\par
%D $ \ifmmode M1\fi x \ifmmode M2\fi $\par
%D $$\ifvmode H1\fi x \ifvmode H2\fi$$\par
%D $$\ifvmode M1\fi x \ifvmode M2\fi$$\par
%D \stoptyping

% We had:
%
% \def\spac_struts_beg
%   {\boundary\plusone
%    \strut
%    \penalty\plustenthousand
%    \hskip\zeroskip}
%
% \def\spac_struts_end
%   {\penalty\plustenthousand
%    \hskip\zeroskip
%    \strut
%    \boundary\plustwo}
%
% We now have (tested for quite a while in cont-exp):

\def\spac_struts_beg
  {\boundary\plusone % user
   \strut
   \wordboundary}

\def\spac_struts_end
  {\wordboundary
   \strut
   \boundary\plustwo} % user

\permanent\protected\def\begstrut
  {\ifmmode
     \strut
   \else
     \dontleavehmode
     \ifcase\struthtdp\else
       \spac_struts_beg
     \fi
   \fi
   \ignorespaces}

\permanent\protected\def\endstrut
  {% \ifmmode M\fi
   % \ifinner I\fi
   \relax\ifhmode
    %\removeunwantedspaces
     \spac_helpers_remove_unwantedspace
     \ifcase\struthtdp\else
       \spac_struts_end
     \fi
   \orelse\ifmmode
    %\removeunwantedspaces hmode only anyway
     \spac_helpers_remove_unwantedspace
     \strut
   \fi}

% unsave:
%
% \def\pseudostrut
%   {\bgroup
%    \setnostrut
%    \normalstrut
%    \egroup}
%
% try:
%
% \startchemie
%   \chemie[ONE,Z0,SB15,MOV1,SB15,Z0][C,C]
% \stopchemie
%
% so:

\permanent\protected\def\pseudostrut
  {\noindent} % better: \dontleavehmode

\aliased\let\pseudobegstrut\pseudostrut
\aliased\let\pseudoendstrut\removeunwantedspaces

\permanent\protected\def\resetteststrut
  {\strutwidth\zeropoint
   \setstrut}

\ifdefined\setfontparameters \else
    \def\setfontparameters{\expandtoks\everybodyfont}
\fi

%D Keyword based strutting:

\letcsname\??struts\v!yes    \endcsname\setstrut
\letcsname\??struts\v!auto   \endcsname\setautostrut
\letcsname\??struts\v!no     \endcsname\setnostrut
\letcsname\??struts\v!cap    \endcsname\setcapstrut
\letcsname\??struts\v!fit    \endcsname\setfontstrut
\letcsname\??struts\v!line   \endcsname\setstrut
\letcsname\??struts\s!default\endcsname\setstrut
\letcsname\??struts\empty    \endcsname\setstrut

%D Handy:

\permanent\def\baselinedistance{\the\lineheight}

%D We need \type {\normaloffinterlineskip} because the new definition contains an
%D assignment, and |<|don't ask me why|>| this assignment gives troubles in for
%D instance the visual debugger.

\pushoverloadmode

\overloaded\permanent\protected\def\offinterlineskip % later we will overload this with a push pop
  {\baselineskip \ignoredepthcriterion % -\thousandpoint
   \lineskip     \zeroskip
   \lineskiplimit\maxdimen
   % We also need this here now; thanks to taco for figuring that out!
   \let\minimumlinedistance\zeropoint}

\overloaded\permanent\protected\def\nointerlineskip
  {\ifvmode\prevdepth\ignoredepthcriterion\fi}% -\thousandpoint

\aliased\let\normaloffinterlineskip\offinterlineskip % knuth's original

\popoverloadmode

%D This is tricky. The prevdepth value is still set to the last one even if there is
%D nothing on the page. The same is true for prevgraf, which doesn't resemble the
%D value on the current page.
%D
%D So, here we kick in a checker but it has to happen after the output group and it
%D only has to be done once (output can trigger itself!).
%D
%D However, prevgraf is somehow bound to hangindent so we can get very nasty side
%D effects. So, in tne end we use our own variable!

\ifdefined\getnofpreviouslines
    % defined public at the lua end
\else
    \let\getnofpreviouslines\!!zerocount
\fi

\protected\def\page_otr_synchronize_page_yes
  {\aftergroup\page_otr_synchronize_page_indeed
   \global\enforced\let\page_otr_synchronize_page\relax}

% This has to become an otr method: \s!page_otr_command_synchonize_page

\protected\def\page_otr_synchronize_page_indeed
  {\ifx\currentoutputroutine\s!multicolumn\else\clf_synchronizepage\fi
   \global\enforced\let\page_otr_synchronize_page\page_otr_synchronize_page_yes}

\let\page_otr_synchronize_page\page_otr_synchronize_page_yes

\appendtoks
    \page_otr_synchronize_page
\to \everyaftershipout

%D My own one:

\protected\def\spac_helpers_push_interlineskip_yes
  {\enforced\edef\oninterlineskip
     {\baselineskip \the\baselineskip
      \lineskip     \the\lineskip
      \lineskiplimit\the\lineskiplimit
      \noexpand\edef\noexpand\minimumlinedistance{\todimension\minimumlinedistance}%
      \enforced\let\noexpand\offinterlineskip\noexpand\normaloffinterlineskip}} % \noexpand not needed

\protected\def\spac_helpers_push_interlineskip_nop
  {\enforced\let\oninterlineskip\setnormalbaselines}

\pushoverloadmode

\overloaded\permanent\protected\def\offinterlineskip
  {\ifdim\baselineskip>\zeropoint
     \spac_helpers_push_interlineskip_yes
   \else
     \spac_helpers_push_interlineskip_nop
   \fi
   \normaloffinterlineskip}

\permanent\let\oninterlineskip\relax

\popoverloadmode

\permanent\protected\def\resetpenalties#1%
  {\ifdefined#1%
     \frozen#1\zerocount
   \fi}

% \permanent\protected\def\setpenalties#1#2#3%
%   {\ifdefined#1% space before #3 prevents lookahead problems, needed when #3=text
%      \frozen#1\numexpr#2+\plusone\relax\space\expandedrepeat\numexpr#2\relax{ #3}\zerocount\relax
%    \fi}

\permanent\protected\def\setpenalties#1#2#3%
  {\ifdefined#1% space before #3 prevents lookahead problems, needed when #3=text
     \frozen#1\ifx#1\brokenpenalties
       \plusone{#3}%
     \else
       {#2+\plusone}%
       \space
       \expandedrepeat{#2}{ #3}%
       \zerocount
     \fi
     \relax
   \fi}

%D \macros
%D   {keeplinestogether}
%D
%D Dirty hack, needed in margin content that can run of a page.

% just before margintexts ... will eventually be done differently in mkiv using
% attributes

\newinteger\c_spac_keep_lines_together

\permanent\protected\lettonothing\restoreinterlinepenalty

\protected\def\spac_penalties_restore
  {\enforced\protected\glettonothing\restoreinterlinepenalty
   \overloaded\global\resetpenalties\interlinepenalties
   \global\c_spac_keep_lines_together\zerocount}

\permanent\protected\def\keeplinestogether#1%
  {\ifnum#1>\c_spac_keep_lines_together
     \global\c_spac_keep_lines_together#1%
     \overloaded\global\setpenalties\interlinepenalties\c_spac_keep_lines_together\plustenthousand
     \global\enforced\let\restoreinterlinepenalty\spac_penalties_restore
   \fi}

%D Here now only deal with penalties that relate to vertical breaking; the
%D horizontal ones have been moved to spac-pas.

\permanent\immutable\integerdef\defaultdisplaywidowpenalty       50
\permanent\immutable\integerdef\defaultwidowpenalty            2000 % was: 1000
\permanent\immutable\integerdef\defaultclubpenalty             2000 % was:  800
\permanent\immutable\integerdef\defaultbrokenpenalty            100

\permanent\immutable\integerdef\defaultgriddisplaywidowpenalty \zerocount
\permanent\immutable\integerdef\defaultgridwidowpenalty        \zerocount
\permanent\immutable\integerdef\defaultgridclubpenalty         \zerocount
\permanent\immutable\integerdef\defaultgridbrokenpenalty       \zerocount

\permanent\protected\def\nopenalties
  {\frozen\widowpenalty \zerocount
   \frozen\clubpenalty  \zerocount
   \frozen\brokenpenalty\zerocount}

\permanent\protected\def\setdefaultpenalties
  {\directsetup{\systemsetupsprefix\s!default}}

\startsetups [\systemsetupsprefix\s!reset]
    \resetpenalties\widowpenalties
    \resetpenalties\clubpenalties
    \resetpenalties\brokenpenalties
    \resetpenalties\interlinepenalties
\stopsetups

% %D We use \directsetup because it's faster and we know there is no csl:
%
% \startsetups [\systemsetupsprefix\s!default]
%
%     \directsetup{\systemsetupsprefix\s!reset}
%
%     \frozen\widowpenalty        \defaultwidowpenalty
%     \frozen\clubpenalty         \defaultclubpenalty
%     \frozen\displaywidowpenalty \defaultdisplaywidowpenalty
%     \frozen\brokenpenalty       \defaultbrokenpenalty
%
% \stopsetups
%
% \startsetups [\v!grid] [\systemsetupsprefix\s!default]
%
%     \directsetup{\systemsetupsprefix\s!reset}
%
%     \frozen\widowpenalty        \defaultgridwidowpenalty
%     \frozen\clubpenalty         \defaultgridclubpenalty
%     \frozen\displaywidowpenalty \defaultgriddisplaywidowpenalty
%     \frozen\brokenpenalty       \defaultgridbrokenpenalty
%
% \stopsetups
%
% %D As an illustration:
%
% \startsetups [\systemsetupsprefix\v!strict]
%
%     \directsetup{\systemsetupsprefix\s!reset}
%
%     \setpenalties \widowpenalties      \plustwo  \maxdimen
%     \setpenalties \clubpenalties       \plustwo  \maxdimen
%     \setpenalties \orphanpenalties     \zerocount
%           \frozen \brokenpenalty                 \maxdimen
%
% \stopsetups

%D We enable this (per 2026-01-06):

\shapingpenalty       \plustenthousand
\shapingpenaltiesmode "FF

%D We need to to set the new ones to the old values (if set) and wipe the old ones so that
%D they don't kick in.

\widowpenalty       \normalwidowpenalty        \normalwidowpenalty       \zerocount
\clubpenalty        \normalclubpenalty         \normalclubpenalty        \zerocount
\displaywidowpenalty\normaldisplaywidowpenalty \normaldisplaywidowpenalty\zerocount
\brokenpenalty      \normalbrokenpenalty       \normalbrokenpenalty      \zerocount
\interlinepenalty   \normalinterlinepenalty    \normalinterlinepenalty   \zerocount

%D Next we define the lot. They can be set with the normal align commands and keys.

\permanent \specificationdef \lesswidowpenalties \widowpenalties
    \plusthree options {
        \largestspecificationoptioncode
       +\doublespecificationoptioncode
       +\constantspecificationoptioncode
    }
    \plusfivethousand    \plusseventyfivehundred
    \plustwohundredfifty \plusfivehundred
    \zerocount           \zerocount
\relax

\permanent \specificationdef \lessclubpenalties \clubpenalties
    \plusthree options {
        \largestspecificationoptioncode
       +\doublespecificationoptioncode
       +\constantspecificationoptioncode
    }
    \plusfivethousand    \plusseventyfivehundred
    \plustwohundredfifty \plusfivehundred
    \zerocount           \zerocount
\relax

\permanent \specificationdef \lessbrokenpenalties \brokenpenalties
    \plusone options {
        \doublespecificationoptioncode
       +\constantspecificationoptioncode
    }
    \plusfifty \plusfivehundred
\relax

\permanent \specificationdef\strictwidowpenalties      \widowpenalties  \plusthree constant \maxcount \maxcount           \zerocount \relax
\permanent \specificationdef\strictwidowpenaltiestwo   \widowpenalties  \plustwo   constant \maxcount                     \zerocount \relax
\permanent \specificationdef\strictwidowpenaltiesthree \widowpenalties  \plusthree constant \maxcount \maxcount           \zerocount \relax
\permanent \specificationdef\strictwidowpenaltiesfour  \widowpenalties  \plusfour  constant \maxcount \maxcount \maxcount \zerocount \relax

\permanent \specificationdef\strictclubpenalties       \clubpenalties   \plusthree constant \maxcount \maxcount           \zerocount \relax
\permanent \specificationdef\strictclubpenaltiestwo    \clubpenalties   \plustwo   constant \maxcount                     \zerocount \relax
\permanent \specificationdef\strictclubpenaltiesthree  \clubpenalties   \plusthree constant \maxcount \maxcount           \zerocount \relax
\permanent \specificationdef\strictclubpenaltiesfour   \clubpenalties   \plusfour  constant \maxcount \maxcount \maxcount \zerocount \relax

\permanent \specificationdef\strictbrokenpenalties     \brokenpenalties \plusone   constant \maxcount                                \relax

\permanent \specificationdef\lessorphanpenalties       \orphanpenalties \plustwo   constant \plusthreethousand \plusonehundred            \relax
\permanent \specificationdef\lessorphanpenaltiestwo    \orphanpenalties \plusone   constant \plusthreethousand                            \relax
\permanent \specificationdef\lessorphanpenaltiesthree  \orphanpenalties \plustwo   constant \plusthreethousand \plusonehundred            \relax
\permanent \specificationdef\lessorphanpenaltiesfour   \orphanpenalties \plusthree constant \plusthreethousand \plusonehundred \plusfifty \relax

\installaligncommand{lesswidows}    {\lesswidowpenalties }
\installaligncommand{lessclubs}     {\lessclubpenalties  }
\installaligncommand{lessbroken}    {\lessbrokenpenalties}

\installaligncommand{lessorphans}   {\lessorphanpenalties     } % == 3
\installaligncommand{lessorphans:2} {\lessorphanpenaltiestwo  }
\installaligncommand{lessorphans:3} {\lessorphanpenaltiesthree}
\installaligncommand{lessorphans:4} {\lessorphanpenaltiesfour }

\installaligncommand{defaultwidows} {\widowpenalty \defaultwidowpenalty }
\installaligncommand{defaultclubs}  {\clubpenalty  \defaultclubpenalty  }
\installaligncommand{defaultbroken} {\brokenpenalty\defaultbrokenpenalty}

\installaligncommand{strictwidows}  {\strictwidowpenalties     }
\installaligncommand{strictwidows:2}{\strictwidowpenaltiestwo  }
\installaligncommand{strictwidows:3}{\strictwidowpenaltiesthree}
\installaligncommand{strictwidows:4}{\strictwidowpenaltiesfour }

\installaligncommand{strictclubs}   {\strictclubpenalties      }
\installaligncommand{strictclubs:2} {\strictclubpenaltiestwo   }
\installaligncommand{strictclubs:3} {\strictclubpenaltiesthree }
\installaligncommand{strictclubs:4} {\strictclubpenaltiesfour  }

\installaligncommand{strictbroken}  {\strictbrokenpenalties    }

%D We also define some math penalty sets. The align commands are more extensive here
%D and we don't want to reset all of them every time we go back to the defaults.
%D This is no real problem because most are not blocking and we will fall through to
%D a next par pass.

\permanent \specificationdef\defaultmathforwardpenalties  \mathforwardpenalties \zerocount
\permanent \specificationdef\defaultmathbackwardpenalties \mathforwardpenalties \zerocount

\permanent \specificationdef\optimalmathforwardpenalties  \mathforwardpenalties  \plusthree constant \plustwohundred \plusonehundred \plusfifty
\permanent \specificationdef\optimalmathbackwardpenalties \mathbackwardpenalties \plusthree constant \plustwohundred \plusonehundred \plusfifty

% align={defaultmath,nonstrictmath}

\startinstallaligncommand[defaultmath]
    \defaultmathforwardpenalties
    \defaultmathbackwardpenalties
    %
    \preshortinlinepenalty    \zerocount
    \postshortinlinepenalty   \zerocount
    \preinlinepenalty         \zerocount
  % \shortinlinemaththreshold \emwidth   % todo
    \mathinlinepenaltyfactor  \zerocount
    \mathdisplaypenaltyfactor \zerocount
\stopinstallaligncommand

%D These are used in the math book that we use for testign all these new paragraph related
%D features.

\startinstallaligncommand[strictmath]
    \optimalmathforwardpenalties
    \optimalmathbackwardpenalties
    %
    \preshortinlinepenalty    \plusthreethousand
  % \postshortinlinepenalty   \plusthreethousand
  % \preinlinepenalty         \zerocount % todo
  % \shortinlinemaththreshold \emwidth   % todo
    \setmathpostpenalty       \mathbinarycode   850\relax
    \setmathpostpenalty       \mathrelationcode 650\relax
  % \mathinlinepenaltyfactor  \zerocount
  % \mathdisplaypenaltyfactor \zerocount
\stopinstallaligncommand

\startinstallaligncommand[nonstrictmath]
    \defaultmathforwardpenalties
    \defaultmathbackwardpenalties
    \preshortinlinepenalty    \zerocount
    \setmathpostpenalty       \mathbinarycode   700
    \setmathpostpenalty       \mathrelationcode 500
\stopinstallaligncommand

% Maybe we need:
%
% \appendtoks
%     \defaultmathforwardpenalties
%     \defaultmathbackwardpenalties
%     \preshortinlinepenalty    \zerocount
%     \postshortinlinepenalty   \zerocount
%     \preinlinepenalty         \zerocount
%     \mathinlinepenaltyfactor  \zerocount
%     \mathdisplaypenaltyfactor \zerocount
% \to \everyforgetall

\startinstallaligncommand[default]
    \widowpenalty \defaultwidowpenalty
    \clubpenalty  \defaultclubpenalty
    \brokenpenalty\defaultbrokenpenalty
\stopinstallaligncommand

\widowpenalty \defaultwidowpenalty
\clubpenalty  \defaultclubpenalty
\brokenpenalty\defaultbrokenpenalty

%D This will be reconsidered:

\startsetups [\systemsetupsprefix\s!default]
    \ifgridlinesnapping
        % gridlinesnapping: to be decided
    \orelse\ifgridsnapping
        \frozen\widowpenalty \defaultgridwidowpenalty
        \frozen\clubpenalty  \defaultgridclubpenalty
        \frozen\brokenpenalty\defaultgridbrokenpenalty
    \else
      % We keep the values that are current and assume no mixed grid / non grid usage.
      %
      % \frozen\widowpenalty \defaultwidowpenalty
      % \frozen\clubpenalty  \defaultclubpenalty
      % \frozen\brokenpenalty\defaultbrokenpenalty
    \fi
\stopsetups

%D This example is now obsolete:
%D
%D \starttyping
%D \startsetups [\systemsetupsprefix\v!strict]
%D     \strictwidowpenalties
%D     \strictclubpenalties
%D     \strictbrokenpenalties
%D \stopsetups
%D \stoptyping

%D For testing:

\permanent\protected\def\nohpenalties{%
    \adjdemerits         \zerocount
    \doublehyphendemerits\zerocount
    \exhyphenpenalty     \zerocount
    \finalhyphendemerits \zerocount
    \hyphenpenalty       \zerocount
    \lefttwindemerits    \zerocount
    \orphanpenalty       \zerocount
    \righttwindemerits   \zerocount
}

\permanent\protected\def\novpenalties{%
    \brokenpenalty        \zerocount
    \clubpenalty          \zerocount
    \interlinepenalty     \zerocount
    \linepenalty          \zerocount
    \shapingpenalty       \zerocount
    %singlelinepenalty    \zerocount
    \toddlerpenalty       \zerocount
    \widowpenalty         \zerocount
}

%D To be checked:

% \newbox\b_spac_struts_saved
%
% \permanent\protected\def\savestrut {\setbox\b_spac_struts_saved\copy\b_strut_box}
% \permanent\protected\def\savedstrut{\copy  \b_spac_struts_saved}

%D Good old blank redone:

%definesystemattribute[kernchars]   [public]
\definesystemattribute[skipcategory][public]
\definesystemattribute[skippenalty] [public]
\definesystemattribute[skiporder]   [public]
\definesystemattribute[snapmethod]  [public]
\definesystemattribute[snapvbox]    [public]
\definesystemattribute[snaprange]   [public]
%definesystemattribute[snapcategory][public]

% TODO: NAMED SNAPPERS

\installcorenamespace{gridsnappers}
\installcorenamespace{gridsnapperattributes}
\installcorenamespace{gridsnappersets}

% \newgluespec \bodyfontlineheight  % see top
% \newdimension\bodyfontstrutheight % see top
% \newdimension\bodyfontstrutdepth  % see top

% \newgluespec \globalbodyfontlineheight  % see top
% \newdimension\globalbodyfontstrutheight % see top
% \newdimension\globalbodyfontstrutdepth  % see top

\permanent\def\snappedvboxattribute
  {\ifgridsnapping
      attr\snapvboxattribute\c_attr_snapmethod
   \fi}

\permanent\def\setlocalgridsnapping
  {\ifgridlinesnapping
     % gridlinesnapping: to be decided
   \orelse\ifgridsnapping
     \c_attr_snapvbox \c_attr_snapmethod
   \fi}

\lettonothing\m_spac_snapper

\def\spac_grids_set_local_snapping#1%
  {\ifgridlinesnapping
     % gridlinesnapping: to be decided
   \orelse\ifgridsnapping
     \ifempty{#1}\else
       \spac_grids_snap_value_set{#1}%
       \c_attr_snapvbox\c_attr_snapmethod
     \fi
   \fi}

\def\spac_grids_expand_snapper#1%
  {\edef\m_spac_snapper
     {\ifempty\m_spac_snapper\else\m_spac_snapper,\fi
      \ifcsname\??gridsnappersets#1\endcsname\lastnamedcs\else#1\fi}}

\lettonothing\p_spac_snapper

\permanent\protected\def\installsnapvalues#1#2%
  {\lettonothing\m_spac_snapper
   \rawprocesscommacommand[#2]\spac_grids_expand_snapper
   \edef\p_spac_snapper{#1:\m_spac_snapper}%
   \ifcsname\??gridsnapperattributes\p_spac_snapper\endcsname
     \scratchcounter\lastnamedcs % already defined
   \else
     \scratchcounter\clf_definesnapmethod{#1}{\m_spac_snapper}%
     \edefcsname\??gridsnapperattributes\p_spac_snapper\endcsname{\the\scratchcounter}%
   \fi
   \edefcsname\??gridsnappers#1\endcsname{\c_attr_snapmethod\the\scratchcounter\relax}%
   \letcsname\??gridsnappersets#1\endcsname\m_spac_snapper}

\permanent\def\theexpandedsnapperset#1{\begincsname\??gridsnappersets#1\endcsname} % only for manuals

\lettonothing\m_spac_grid_asked

\permanent\protected\def\usegridparameter#1% no checking here
  {\edef\m_spac_grid_asked{#1\c!grid}%
   \ifempty\m_spac_grid_asked
     \c_attr_snapvbox\attributeunsetvalue
   \else
     \spac_grids_snap_value_set\m_spac_grid_asked
     \c_attr_snapvbox\c_attr_snapmethod
   \fi}

% This could become (after being tested):
%
% \permanent\protected\def\usegridparameter#1% no checking here
%   {\ifempty{#1\c!grid}%
%      \c_attr_snapvbox\attributeunsetvalue
%    \else
%      \expanded{\spac_grids_snap_value_set{\lastnamedcs}}%
%      \c_attr_snapvbox\c_attr_snapmethod
%    \fi}

\permanent\tolerant\protected\def\definegridsnapping[#1]#*[#2]%
  {\installsnapvalues{#1}{#2}}

\protected\edef\spac_grids_snap_value_reset
  {%\gridsnappingfalse
   \c_attr_snapmethod\attributeunsetvalue}

\protected\def\spac_grids_snap_value_set#1%
  {%\gridsnappingtrue
   \begincsname\??gridsnappers#1\endcsname}

\permanent\protected\def\resetgridsnapping
  {\gridlinesnappingfalse
   \gridsnappingfalse
   %resetsystemmode\v!grid
   \spac_grids_snap_value_reset}

\def\spac_grids_snap_value_auto#1%
  {\ifcsname\??gridsnappers#1\endcsname
     \lastnamedcs
   \else
     \installsnapvalues\s!dummy{#1}%
     \csname\??gridsnappers\s!dummy\endcsname
   \fi}

% \installsnapvalues{loose} {\v!maxdepth:0.8,\v!maxheight:0.8,\v!strut}
% \installsnapvalues{normal}{\v!maxdepth:1.0,\v!maxheight:1.0,\v!strut}
% \installsnapvalues{tight} {\v!maxdepth:1.2,\v!maxheight:1.2,\v!strut}

% none             don't enlarge
% halfline         enlarge by halfline/halfline
% line             enlarge by line/line
% strut            enlarge by ht/dp (default)
% first            align to top line
% last             align to bottom line
% mindepth         round depth down
% maxdepth         round depth up
% minheight        round height down
% maxheight        round height up
% local            use local interline space
% offset:-3tp      vertical shift within box
% bottom:lines
% top:lines
% box              centers a box rounded upwards (box:.5 -> tolerance)
% min              centers a box rounded downwards
% max              centers a box rounded upwards

%D We're not downward compatible with \MKII ! Not yet in interface file:

\definegridsnapping[\v!normal]           [\v!maxheight,\v!maxdepth,\v!strut]
\definegridsnapping[\v!standard]         [\v!maxheight,\v!maxdepth,\v!strut]
\definegridsnapping[\v!yes]              [\v!maxheight,\v!maxdepth,\v!strut]

\definegridsnapping[\v!strict]           [\v!maxdepth:0.8,\v!maxheight:0.8,\v!strut]
\definegridsnapping[\v!tolerant]         [\v!maxdepth:1.2,\v!maxheight:1.2,\v!strut]
\definegridsnapping[\v!verytolerant]     [\v!maxdepth:1.4,\v!maxheight:1.4,\v!strut]

\definegridsnapping[\v!tolerant:10]      [\v!maxdepth:1.1,\v!maxheight:1.1,\v!strut] % 10 pct tolerance
\definegridsnapping[\v!tolerant:20]      [\v!maxdepth:1.2,\v!maxheight:1.2,\v!strut] % 20 pct tolerance
\definegridsnapping[\v!tolerant:30]      [\v!maxdepth:1.3,\v!maxheight:1.3,\v!strut] % 30 pct tolerance
\definegridsnapping[\v!tolerant:40]      [\v!maxdepth:1.4,\v!maxheight:1.4,\v!strut] % 40 pct tolerance

\definegridsnapping[\v!top]              [\v!minheight,\v!maxdepth,\v!strut]
\definegridsnapping[\v!bottom]           [\v!maxheight,\v!mindepth,\v!strut]
\definegridsnapping[\v!both]             [\v!minheight,\v!mindepth,\v!strut]

\definegridsnapping[\v!broad]            [\v!maxheight,\v!maxdepth,\v!strut,0.8] % maybe 0.85
\definegridsnapping[\v!fit]              [\v!maxheight,\v!maxdepth,\v!strut,1.2] % tight 0.15

\definegridsnapping[\v!first]            [\v!first]
\definegridsnapping[\v!last]             [\v!last]
\definegridsnapping[\v!high]             [\v!minheight,\v!maxdepth,\v!none]
\definegridsnapping[\v!one]              [\v!minheight,\v!mindepth]
\definegridsnapping[\v!low]              [\v!maxheight,\v!mindepth,\v!none]
\definegridsnapping[\v!none]             [\v!none]
\definegridsnapping[\v!line]             [\v!line]
\definegridsnapping[\v!strut]            [\v!strut]
\definegridsnapping[\v!box]              [\v!box]
\definegridsnapping[\v!min]              [\v!min]
\definegridsnapping[\v!max]              [\v!max]

\definegridsnapping[\v!middle]           [\v!maxheight,\v!maxdepth] % used in placement

% maybe don't document these

\definegridsnapping[\v!math]             [\v!maxdepth:1.05,\v!maxheight:1.05,\v!strut] % experimental, maybe 1.1
\definegridsnapping[\v!math:\v!line]     [\v!math,\v!line,\v!split]
\definegridsnapping[\v!math:\v!halfline] [\v!math,\v!halfline,\v!split]
\definegridsnapping[\v!math:-\v!line]    [\v!math,-\v!line,\v!split]
\definegridsnapping[\v!math:-\v!halfline][\v!math,-\v!halfline,\v!split]

\permanent\protected\def\synchronizelocallinespecs
  {\bodyfontlineheight \normallineheight
   \bodyfontstrutheight\strutht
   \bodyfontstrutdepth \strutdp}

\permanent\protected\def\synchronizegloballinespecs
  {\global\globalbodyfontlineheight \normallineheight
   \global\globalbodyfontstrutheight\strutht
   \global\globalbodyfontstrutdepth \strutdp}

\appendtoks
    \synchronizegloballinespecs
    \synchronizelocallinespecs
\to \everysetupglobalinterlinespace

\appendtoks
    \synchronizelocallinespecs
\to \everysetuplocalinterlinespace

%D More might be added here:

% \def\restoreglobalinterlinespace
%   {\mutable\let\normallineheight\globalbodyfontlineheight % mutable crashed at some point
%    \bodyfontlineheight \globalbodyfontlineheight          % s we have a bug deep down
%    \bodyfontstrutheight\globalbodyfontstrutheight
%    \bodyfontstrutdepth \globalbodyfontstrutdepth}

\permanent\protected\def\restoreglobalinterlinespace
  {\let\normallineheight\globalbodyfontlineheight
   \bodyfontlineheight \globalbodyfontlineheight
   \bodyfontstrutheight\globalbodyfontstrutheight
   \bodyfontstrutdepth \globalbodyfontstrutdepth}

% \appendtoks
%     \restoreglobalinterlinespace % done elsewhere
% \to \everybeforepagebody

%D We still have to synchronize these:

% \permanent\protected\def\synchronizeskipamounts
%   {\bigskipamount
%                  \skipfactor\baselineskip
%       \s!plus\skipgluefactor\baselineskip
%      \s!minus\skipgluefactor\baselineskip
%    \relax
%    \medskipamount  \bigskipamount \divideby\medskipamount  \plustwo
%    \smallskipamount\bigskipamount \divideby\smallskipamount\plusfour}

\permanent\protected\def\synchronizeskipamounts
  {\bigskipamount
     \skipfactor\baselineskip
     \ifgridlinesnapping
       % fixed
     \orelse\ifgridsnapping
       % fixed
     \else
       \s!plus \skipgluefactor\baselineskip
       \s!minus\skipgluefactor\baselineskip
     \fi
   \relax
   \medskipamount  \bigskipamount \divideby\medskipamount  \plustwo
   \smallskipamount\bigskipamount \divideby\smallskipamount\plusfour}


%D Snapping.

\permanent\tolerant\protected\def\startgridsnapping[#1]%
  {\snaptogrid[#1]\vbox\bgroup}

\permanent\protected\def\stopgridsnapping
  {\egroup}

\permanent\tolerant\protected\def\placeongrid[#1]%
  {\snaptogrid[#1]\vbox} % mark as done

\permanent\tolerant\protected\def\snaptogrid[#1]% list or predefined
  {\ifgridlinesnapping
     % gridlinesnapping: to be decided
     \expandafter\gobbleoneargument
   \orelse\ifgridsnapping
     \expandafter\spac_grids_snap_to_indeed
   \else
     \expandafter\gobbleoneargument
   \fi{#1}}

\def\spac_grids_snap_to_indeed#1%
  {\bgroup
   \spac_grids_snap_value_reset
   \dowithnextbox{\spac_grids_snap_to_finish{#1}}}

% eventually there will always be a line snap

\def\spac_grids_snap_to_finish#1%
  {\ifvbox\nextbox % this will go away
     \clf_vspacingcollapse\nextbox\relax % isn't that already done?
   \fi
   \ifempty{#1}%
     \spac_grids_snap_value_set\v!normal
   \else
     \spac_grids_snap_value_set{#1}%
   \fi
   \clf_vspacingsnap\nextbox\c_attr_snapmethod\relax
   \ifvbox\nextbox\vbox\else\hbox\fi attr \snapmethodattribute \zerocount {\box\nextbox}% no pack (?), we snap
   \egroup}

\permanent\protected\def\setupgridsnapping[#1]% less overhead than setuplayout (needs testing)
  {\setlayoutparameter\c!grid{#1}\synchronizegridsnapping}

\mutable\lettonothing\checkedgridmethod % this can become private

\mutable\let\checkedgridscope\v!local % this can become private

\permanent\protected\def\checkgridmethod#1%
  {\edef\p_grid{#1}%
   \ifempty\p_grid
     \lettonothing\checkedgridmethod
     \let\checkedgridscope\v!local
   \else
     \splitatcolon\p_grid\checkedgridscope\checkedgridmethod
     \ifempty\checkedgridmethod
       \ifx\checkedgridscope\v!local\orelse\ifx\checkedgridscope\v!global\else
         \let\checkedgridmethod\checkedgridscope
         \let\checkedgridscope \v!local
       \fi
     \fi
   \fi}

\permanent\protected\def\applygridmethod#1#2#3% content localsettings (used in head rendering)
   {\checkgridmethod{#1}%
    \ifx\checkedgridscope\v!global
      \ifempty\checkedgridmethod \else
        % we assume that the call is grouped because grouping here has the side
        % effect that the eventually constructed line will get the value outside
        % the group
        %
        % overkill: \setupgridsnapping[\checkedgridmethod]%
        % maybe   : \spac_grids_snap_value_auto\checkedgridmethod
        \spac_grids_snap_value_set\checkedgridmethod
      \fi
      \hbox{#3}%
    \else
      % the extra hbox will trigger the global snapper on top of the local and
      % we really need that in this case (compatibility etc etc) so here we don't
      % het an already done hit (otherwise we would not snap)
      \hbox\bgroup
        \ifempty\checkedgridmethod\orelse\ifconditional\headisdisplay
          #2%
        \fi
        \snaptogrid[\checkedgridmethod]\hbox{#3}%
      \egroup
    \fi}

\protected\gdef\page_layouts_calculate_overshoot
  {\ifgridlinesnapping
     % gridlinesnapping: to be decided
   \orelse\ifgridsnapping
     \ifcase\layoutlines
       \getnoflines\textheight
       \textovershoot{\noflines\globalbodyfontlineheight-\textheight}%
     \fi
   \fi}

\protected\def\page_layouts_report_overshoot
  {\page_layouts_calculate_overshoot
   \ifdim\textovershoot>\zeropoint
     \writestatus\m!layouts{gridmode,\space
       noflines: \the\noflines,\space
       textheight: \the\textheight,\space
       textovershoot: \the\textovershoot\space
       (maybe set number of lines instead)%
    }%
  \fi
  \glet\page_layouts_report_overshoot\page_layouts_calculate_overshoot}

\appendtoks
    \page_layouts_report_overshoot
\to \everybeforepagebody

%D Visualization:

\definepalet
  [grid]
  [  one=red,
     two=green,
   three=blue,
    four=gray]

\permanent\protected\def\setgridtracebox#1[#2]% % maybe reverse the order
  {\setbox\nextbox#1%
     {\hbox
        {\hbox to \zeropoint
           {\setlayoutcomponentattribute{\v!grid:\v!test}%
            \color[grid:#2]{\ruledhbox \layoutcomponentboxattribute {\fakebox\nextbox}}%
            \hss}%
         \flushnextbox}}}

\setnewconstant\gridboxlinenomode\plusone % 0:nothing 1:all 2:lines 3:frame 4:l/r
\setnewconstant\gridboxlinemode  \plusone

\permanent\protected\def\gridboxvbox
  {\ifcase\gridboxlinemode
     \vpack
   \or
     \ruledvpack
   \or
     \vpack
   \or
     \ruledvpack
   \else
     \ruledvpack
   \fi}

\permanent\def\gridboxwidth{\ifcase\gridboxlinemode0\or.5\or.5\or0\else.5\fi\linewidth}

\permanent\protected\def\setgridbox#1#2#3%
  {\setbox#1\gridboxvbox to #3 % given size
     {\forgetall
      \resetvisualizers
      \resetteststrut
      \offinterlineskip
      \hsize#2%
      \ifcase\gridboxlinenomode\or\or\or
        \gridboxlinenomode\doifoddpageelse\plusone\plustwo % 3: outer
      \or
        \gridboxlinenomode\doifoddpageelse\plustwo\plusone % 4: inner
      \fi
      \topskipcorrection
      \gridboxvbox % calculated size
        {\getrawnoflines{#3}% \getnoflines{#3}%
         \scratchdimen{#2+\lineheight}%
         \localcontrolledrepeat\noflines
           {\strut
            \hskip-.5\lineheight\relax
            \ifcase\gridboxlinenomode\or
              \rlap
                {\kern{.2\bodyfontsize+\scratchdimen}%
                 \infofont\hbox to \emwidth{\hss\the\currentloopiterator}}%
            \or
              \llap
                {\infofont\hbox to \emwidth{\hss\the\currentloopiterator}%
                 \kern.2\bodyfontsize}%
            \fi
            \vrule
              \s!height \gridboxwidth
              \s!depth  \gridboxwidth
              \s!width  \scratchdimen
            \par}}%
      \vfill}}

%D This has become obsolete:

% \def\moveboxontogrid#1#2#3% will become obsolete, but it needs checking
%   {}

%D Helper:

\protected\def\spac_helpers_assign_skip#1#2% ook nog \v!halfline+fuzzysnap
  {\ifcstok{#2}\v!line
     #1%
     \ifgridlinesnapping
       \bodyfontlineheight
     \orelse\ifgridsnapping
       \bodyfontlineheight
     \else
       \openlineheight
     \fi
   \else
     \ifgridlinesnapping
       \assigndimension{#2}{#1}{.25\bodyfontlineheight}{.5\bodyfontlineheight}\bodyfontlineheight
     \orelse\ifgridsnapping
       \assigndimension{#2}{#1}{.25\bodyfontlineheight}{.5\bodyfontlineheight}\bodyfontlineheight
     \else
       \assigndimension{#2}{#1}\smallskipamount\medskipamount\bigskipamount
     \fi
   \fi
   \relax}

% \start \dosetstretch{.25em} \setuptolerance[tolerant,stretch] \input tufte \endgraf \stop
% \start \dosetstretch{.5em} effe flink doorfietsen \stop

% experimental code, not yet interfaced:

% category:
%
%  0 == discard                             discard
%  1 == only if larger                      largest
%  2 == force even if smaller               force
%  3 == only take penalty component         penalty
%  4 == add to existing skip                add
%  5 == disable (ignore following)          disable
%  6 == kill whitespace                     nowhite
%  7 == discard previous                    back
% 10 == no topskip
%
% penalty: larger wins
% order: larger wins
% category:2,order:5,penalty:10000,skip:value|kw
%
% \defineblankmethod  [\v!joinedup] {\ifvmode\nointerlineskip\fi}

% todo, in grid mode: builders.vspacing.fixed = false

\installcorenamespace{vspacing}
\installcorenamespace{vspacingamount}

\permanent\tolerant\protected\def\definevspacingamount[#1]#*[#2]#*[#3]% can be combined
  {\ifarguments
     %defcsname\??vspacingamount#1\endcsname
     % {\lineheight}%
   \or
     \defcsname\??vspacingamount#1\endcsname
       {\lineheight}%
   \or
     \defcsname\??vspacingamount#1\endcsname
       {\ifgridlinesnapping
          \lineheight
        \orelse\ifgridsnapping
          \lineheight
        \else
          #2%
        \fi}%
   \or
     \defcsname\??vspacingamount#1\endcsname
       {\ifgridlinesnapping
          #3%
        \orelse\ifgridsnapping
          #3%
        \else
          #2%
        \fi}%
   \fi
   \clf_vspacingsetamount{#1}}

\def\spac_vspacing_no_topskip % use grouped
  {\c_attr_skipcategory\pluseleven}

%permanent\def\vspacingfromscratchtoks  {\scratchdimen{\csname\??vspacingamount\the\scratchtoks\endcsname}}
\permanent\def\vspacingpredefinedvalue#1{\s_spac_vspacing_predefined{\csname\??vspacingamount#1\endcsname}}
%permanent\def\vspacingfromtempstring   {\scratchdimen{\csname\??vspacingamount\tempstring\endcsname}}

% \installcorenamespace{vspacingamountnormal}
% \installcorenamespace{vspacingamountgrid}

% \def\spac_vspacing_define_amount[#1][#2][#3]% can be combined
%   {\ifcsname n>#1\endcsname\else
%      \expandafter\newtoks\csname n>#1\endcsname
%      \expandafter\newtoks\csname g>#1\endcsname
%    \fi
%    \csname n>#1\endcsname{#2}%
%    \csname g>#1\endcsname{#3}%
%    \clf_vspacingsetamount{#1}}

\permanent\tolerant\protected\def\definevspacing[#1]#*[#2]%
  {\clf_vspacingdefine{#1}{#2}}

%D The injector code (generated at the \LUA\ end). This will go away!

% \newtoks\everybeforeblankhandling
% \newtoks\everyafterblankhandling
%
% \newconditional\c_space_vspacing_done
% \newconditional\c_space_vspacing_fixed
% \newconditional\c_space_ignore_parskip
%
% \appendtoks
%     \s_spac_vspacing_temp\zeropoint
%     \c_attr_skipcategory\plusone
%     \c_attr_skippenalty \attributeunsetvalue
%     \c_attr_skiporder   \attributeunsetvalue
%     \ifgridlinesnapping
%         \c_space_vspacing_fixed\conditionaltrue
%     \orelse\ifgridsnapping
%         \c_space_vspacing_fixed\conditionaltrue
%     \else
%         \c_space_vspacing_fixed\conditionalfalse
%     \fi
% \to \everybeforeblankhandling
%
% \appendtoks
%     \s_spac_vspacing_temp\plusone\s_spac_vspacing_temp
%       \ifconditional\c_space_vspacing_fixed \else
%         \s!plus \skipgluefactor\s_spac_vspacing_temp
%         \s!minus\skipgluefactor\s_spac_vspacing_temp
%       \fi
%     \relax
% \to \everyafterblankhandling

% The main spacer:

\permanent\tolerant\protected\def\vspacing[#1]%
  {\ifarguments
     \spac_vspacing_yes[\currentvspacing]% in the new mechanism no [] needed
   \else
     \spac_vspacing_yes[#1]% in the new mechanism no [] needed
   \fi}

\def\spac_vspacing_yes
  {\ifinpagebody % somewhat weird
     \expandafter\spac_vspacing_yes_indeed
   \orelse\ifconditional\c_spac_packed_blank
     \expandafter\spac_vspacing_yes_indeed
   \else
     \expandafter\spac_vspacing_yes_ignore
   \fi}

\def\spac_vspacing_nop
  {\ifinpagebody % somewhat weird
     \expandafter\spac_vspacing_nop_indeed
   \orelse\ifconditional\c_spac_packed_blank
     \expandafter\spac_vspacing_nop_indeed
   \else
     \expandafter\spac_vspacing_nop_ignore
   \fi}

\def\spac_vspacing_yes_indeed[#1]%
  {\ifmmode\else\par\ifvmode
     \clf_injectvspacing
       \ifgridlinesnapping\plusone\orelse\ifgridsnapping\plusone\else\zerocount\fi
       {#1}%
   \fi\fi}

\def\spac_vspacing_nop_indeed
  {\ifmmode\else\par\ifvmode
     \clf_injectvspacing
       \ifgridlinesnapping\plusone\orelse\ifgridsnapping\plusone\else\zerocount\fi
       {\currentvspacing}%
   \fi\fi}

\def\spac_vspacing_yes_ignore[#1]{\ifmmode\else\par\fi}
\def\spac_vspacing_nop_ignore    {\ifmmode\else\par\fi}

\permanent\protected\def\directvspacing#1%
 {\ifmmode\else\par\ifvmode
    \clf_injectvspacing
       \ifgridlinesnapping\plusone\orelse\ifgridsnapping\plusone\else\zerocount\fi
      {\ifempty{#1}\currentvspacing\else#1\fi}%
  \fi\fi}

\permanent\protected\def\directdefaultvspacing
  {\ifinpagebody % somewhat weird
     \directvspacing\currentvspacing
   \orelse\ifconditional\c_spac_packed_blank
     \directvspacing\currentvspacing
   \fi}

\permanent\protected\def\directcheckedvspacing
  {\ifinpagebody % somewhat weird
     \expandafter\directvspacing
   \orelse\ifconditional\c_spac_packed_blank
     \expandafter\directvspacing
   \else
     \expandafter\gobbleoneargument
   \fi}

\lettonothing\m_spac_blank_asked

\permanent\protected\def\useblankparameter#1% faster local variant
  {\edef\m_spac_blank_asked{#1\c!blank}%
   \ifempty\m_spac_blank_asked\else
     \directvspacing\m_spac_blank_asked
   \fi}

% This could be (needs to be tested):
%
% \permanent\protected\def\useblankparameter#1% faster local variant
%   {\ifempty{#1\c!blank}\else
%      \expanded{\directvspacing{\lastnamedcs}}%
%    \fi}

%D Handy (and faster):

\permanent\protected\def\directvpenalty#1{\ifmmode\else\par\ifvmode\clf_injectvpenalty#1\relax\fi\fi}
\permanent\protected\def\directvskip   #1{\ifmmode\else\par\ifvmode\clf_injectvskip   #1\relax\fi\fi}

%D These depend on bigskipamount cum suis so we'd better sync them:

\mutable\let\currentvspacing\s!default % hm, default, standard ...

\permanent\tolerant\protected\def\setupvspacing[#1]%
  {\ifarguments\else
     \edef\currentvspacing{#1}%
   \fi
   \synchronizevspacing}

\permanent\protected\def\synchronizevspacing
  {\ifempty\currentvspacing % mistakenly had an \else
     \let\currentvspacing\s!default
   \fi
   \spac_whitespace_setup_nop}

\permanent\protected\def\restorestandardblank % or default ?
  {\let\currentvspacing\v!standard}

%D The \type {category:4} is default.

\definevspacingamount[\v!none]        [\zeroskip]          [\zeroskip]
\definevspacingamount[\v!big]         [\bigskipamount]     [\bodyfontlineheight]
\definevspacingamount[\v!medium]      [\medskipamount]     [.5\bodyfontlineheight]
\definevspacingamount[\v!small]       [\smallskipamount]   [.25\bodyfontlineheight]
\definevspacingamount[\v!line]        [\openlineheight]    [\bodyfontlineheight]
\definevspacingamount[\v!halfline]    [.5\openlineheight]  [.5\bodyfontlineheight]
\definevspacingamount[\v!quarterline] [.25\openlineheight] [.25\bodyfontlineheight]
\definevspacingamount[\v!formula]     [\medskipamount]     [.5\bodyfontlineheight]
\definevspacingamount[\v!white]       [\parskip]           [\bodyfontwhitespace]
\definevspacingamount[\v!height]      [\strutht]           [\bodyfontstrutheight]
\definevspacingamount[\v!depth]       [\strutdp]           [\bodyfontstrutdepth]

\definevspacingamount[\v!standard]    [.75\openlineheight] [.75\openlineheight] % mkii compatible

% for math formulas (in balanced grid mode aka columnsets method=mvl)

\definevspacing[halflinebefore][\v!keep,\v!halfline]
\definevspacing[halflineafter] [\v!halfline]

\permanent\def\bodyfontwhitespace
  {\dimexpr
     \ifzeropt\parskip
       \zeropoint
     \orelse\ifgridlinesnapping
       \bodyfontlineheight
     \orelse\ifgridsnapping
       \bodyfontlineheight
     \else
       \parskip
     \fi
   \relax}

%D used in itemize \unknown\ always test this:

\def\spac_overlay_lines
  {\directcheckedvspacing{\v!back,\v!overlay}% \blank[\v!back,\v!overlay]%
   \nointerlineskip}

% \startitemize[n]
%     \item \input zapf
%     \item \startitemize[a]
%         \item \input knuth
%     \stopitemize
% \stopitemize
%
% \strut \hfill first line \blank[overlay] second line \hfill \strut
%
% \ruledvbox {
%     \strut \hfill line 1 \blank[overlay]
%     line 2 \hfill \strut \blank[overlay]
%     \strut \hfill line 3 \hfill \strut
% }
%
% \dorecurse{50}
%   {\startitemize[n] \startitem \startitemize[a] \item #1 \stopitemize \stopitem \stopitemize}

\definevspacing[\v!preference][penalty:-500]  % goodbreak
\definevspacing[\v!samepage]  [penalty:10000] % nobreak

\definevspacing[\v!always]    [category:0] % hm, internally it's discard so is this used / okay
\definevspacing[\v!max]       [category:1]
\definevspacing[\v!force]     [category:2]
\definevspacing[\v!disable]   [category:5]
\definevspacing[\v!nowhite]   [category:6]
\definevspacing[\v!back]      [category:7]
\definevspacing[\v!packed]    [category:8]  % noparskip (kind of special)
\definevspacing[\v!overlay]   [category:9]
\definevspacing[\v!enable]    [category:10]
%definevspacing[\v!noparskip] [category:8]
%definevspacing[\v!notopskip] [category:11]
\definevspacing[\v!keep]      [category:12]

\definevspacing[\v!weak]      [order:0]
\definevspacing[\v!strong]    [order:100]

\definevspacing[\s!default]   [\v!white] % was big for a while

\newinteger\c_spac_vspacing_special_base \c_spac_vspacing_special_base = 32250 % 4000
\newinteger\c_spac_vspacing_special_step \c_spac_vspacing_special_step =    10 %  250
\newinteger\c_spac_vspacing_special_done

% 2019-05-31 : upgraded a bit to more distinctive samepage-[level]-[0|1|2] names

\protected\def\spac_vspacing_define_same_step#1#2% alternatively we could have samepage-n-m
  {\begingroup
   \scratchcounterone{\plusthree*#1+#2}%
   \scratchcountertwo{\c_spac_vspacing_special_base+\c_spac_vspacing_special_step*\scratchcounterone}%
  %\writestatus{defined}{\v!samepage-\number#1-\number#2\space=>\space penalty:\the\scratchcountertwo}%
   \expanded{\definevspacing[\v!samepage-\number#1-\number#2][penalty:\the\scratchcountertwo]}%
   \endgroup}

% \protected\def\spac_vspacing_define_same_page#1%
%   {\dostepwiserecurse\c_spac_vspacing_special_done{#1}\plusone
%      {\spac_vspacing_define_same_step\recurselevel\zerocount % before
%       \spac_vspacing_define_same_step\recurselevel\plusone   % after
%       \spac_vspacing_define_same_step\recurselevel\plustwo}% % whatever
%    \global\c_spac_vspacing_special_done#1\relax}

\protected\def\spac_vspacing_define_same_page#1%
  {\localcontrolledloop\c_spac_vspacing_special_done=#1=\plusone
     {\spac_vspacing_define_same_step\currentloopiterator\zerocount % before
      \spac_vspacing_define_same_step\currentloopiterator\plusone   % after
      \spac_vspacing_define_same_step\currentloopiterator\plustwo}% % whatever
   \global\c_spac_vspacing_special_done#1\relax}

\spac_vspacing_define_same_page{12} % 12 levels should be more than enough as a start

% \def\spac_vspacing_same_page#1#2% level offset (starts at 0)
%   {\ifnum#1>\c_spac_vspacing_special_done
%      \spac_vspacing_define_same_page{#1}%
%    \fi
%   %\writestatus{used}{\v!samepage-\number#1-\number#2}%
%    \vspacing[\v!samepage-\number#1-\number#2]}

\def\spac_vspacing_same_page#1#2% level offset (starts at 0)
  {\ifnum#1<\zerocount\else
     \ifnum#1>\c_spac_vspacing_special_done
       \spac_vspacing_define_same_page{#1}%
     \fi
    %\writestatus{used}{\v!samepage-\number#1-\number#2}%
     \vspacing[\v!samepage-\number#1-\number#2]%
   \fi}

\definevspacing[\v!default]  [\v!big]      % todo: needs to adapt to \setupblank
\definevspacing[\v!before]   [\v!default]  % but we need to avoid circular references
\definevspacing[\v!inbetween][\v!default]  % then
\definevspacing[\v!after]    [\v!before]

\setupvspacing
  [\v!big] % alternatively [\v!standard]

%D Maybe at some point we will differ between \type {\vspacing} and \type {\blank}
%D (we needed the first one while playing with the new code).

% We keep this one as reference
%
% \protected\def\inhibitblank
%   {\vspacing[\v!disable]}
%
% but use the following more efficient variant instead:

\permanent\protected\def\inhibitblank
  {\ifmmode\else\par\ifvmode
     \clf_injectdisable
   \fi\fi}

\permanent\protected\def\inhibitwhitespace
  {\ifmmode\else\par\ifvmode
     \clf_injectnowhite
   \fi\fi}

\permanent\protected\def\inhibitallspacing
  {\ifmmode\else\par\ifvmode
     \clf_injectdisable
     \clf_injectnowhite
   \fi\fi}

\aliased\let\doinhibitblank\inhibitblank % keep this command for a while, used in styles

%D We use \type {vspacing} because at some point we had the blank mechanism alongside
%D a new experimental variant and I aliased it locally till it all worked out well. So,
%D we're kind of stuck with synonyms now. Some day we will drop the vspacing and use the
%D old aliases instead.

\aliased\let\defineblank      \definevspacing
\aliased\let\setupblank       \setupvspacing
\aliased\let\blank            \vspacing
\aliased\let\synchronizeblank \synchronizevspacing
\aliased\let\defineblankmethod\definevspacingamount

%D The following command is for Wolfgang. It has to be used with care as it does
%D {\em not} work in tandem with the other spacing commands.

\installcorenamespace{vspace}

\permanent\tolerant\protected\def\definevspace[#1]#*[#2]#*[#3]%
  {\ifarguments
   \or
     \letcsname\??vspace:#1\endcsname\empty
   \or
     \defcsname\??vspace:#1\endcsname{#2}%
   \or
     \defcsname\??vspace#1:#2\endcsname{#3}%
   \fi}

\letcsname\??vspace:\s!unknown\endcsname\zeropoint

\def\spac_vspace_unknown
  {\csname\??vspace:\s!unknown\endcsname} % why not a let?

\permanent\tolerant\protected\def\vspace[#1]#*[#2]%
  {\par
   \ifvmode
     \removelastskip
     \vskip
       \ifparameter#2\or
         \ifcsname\??vspace#1:#2\endcsname
           \lastnamedcs
         \orelse\ifcsname\??vspace:#2\endcsname
           \lastnamedcs
         \else
           \spac_vspace_unknown
         \fi
       \orelse\ifparameter#1\or
         \ifcsname\??vspace:#1\endcsname
           \lastnamedcs
         \else
           \spac_vspace_unknown
         \fi
       \else
         \ifcsname\??vspace:\s!default\endcsname
           \lastnamedcs
         \else
           \spac_vspace_unknown
         \fi
       \fi
     \relax
   \fi}

%D Some preliminary code: a simple and fast hanger, for usage in macros.

\installcorenamespace {hanging}

\installdirectcommandhandler \??hanging {hanging}

% \setuphanging
%   [\c!distance=.5\emwidth,
%    \c!location=\v!left,
%    \c!n=\zerocount]
%
% \lettonothing\m_spac_hanging_location
%
% \permanent\tolerant\protected\def\starthanging[#1]%
%   {\dontleavehmode\bgroup
%    \ifhastok={#1}%
%      \lettonothing\m_spac_hanging_location
%      \setupcurrenthanging[#1]%
%    \else
%      \edef\m_spac_hanging_location{#1}%
%    \fi
%    \ifempty\m_spac_hanging_location
%      \edef\m_spac_hanging_location{\directhangingparameter\c!location}%
%    \fi
%    \dowithnextboxcs\spac_hanging_finish\hbox}
%
% \permanent\protected\def\stophanging
%   {\endgraf
%    \egroup}
%
% \def\spac_hanging_finish
%   {\scratchdistance\directhangingparameter\c!distance\relax
%    \ifdim\ht\nextbox>\strutht
%      \setbox\nextbox\tbox{\box\nextbox}%
%    \fi
%    \scratchcounter\directhangingparameter\c!n\relax
%    \ifnum\scratchcounter>\zerocount
%      \frozen\hangafter-\scratchcounter
%    \else
%      \scratchdimen\htdp\nextbox
%      \getnoflines\scratchdimen
%      \frozen\hangafter-\noflines
%    \fi
%    \ht\nextbox\strutht
%    \dp\nextbox\strutdp
%    \scratchwidth\dimexpr\wd\nextbox+\scratchdistance\relax
%    \ifx\m_spac_hanging_location\v!right
%      \frozen\hangindent\ifconditional\displaylefttoright-\fi\scratchwidth
%      \rlap{\kern\dimexpr\hsize-\leftskip-\wd\nextbox\relax\box\nextbox}% \leftskip is new
%    \else
%      \frozen\hangindent\ifconditional\displaylefttoright\else-\fi\scratchwidth
%      \llap{\box\nextbox\kern\scratchdistance}%
%    \fi
%    \ignorespaces}

\setuphanging
  [\c!distance=.5\emwidth,
   \c!location=\v!left,
   \c!topoffset=\zeropoint,
   \c!bottomoffset=\zeropoint,
   \c!n=\zerocount]

\lettonothing\m_spac_hanging_location

\permanent\tolerant\protected\def\starthanging[#1]%
  {\par
   \begingroup
   \ifhastok={#1}%
     \lettonothing\m_spac_hanging_location
     \setupcurrenthanging[#1]%
   \else
     \edef\m_spac_hanging_location{#1}%
   \fi
   \ifempty\m_spac_hanging_location
     \edef\m_spac_hanging_location{\directhangingparameter\c!location}%
   \fi
   \dowithnextboxcs\spac_hanging_finish\hbox}

\def\spac_hanging_finish
  {\ifdim\ht\nextbox>\strutht
     \setbox\nextbox\tbox{\box\nextbox}%
   \fi
   \scratchdistance    {\directhangingparameter\c!distance}%
   \scratchcounter     {\directhangingparameter\c!n}%
   \scratchtopoffset   {\directhangingparameter\c!topoffset}%
   \scratchbottomoffset{\directhangingparameter\c!bottomoffset}%
   \ifnum\scratchcounter>\zerocount
     \noflines\scratchcounter
   \else
     \scratchdimen{%
       \htdp\nextbox
       +\scratchtopoffset
       +\scratchbottomoffset
     }%
     \getnoflines\scratchdimen
   \fi
   \boxyoffset\nextbox-\scratchtopoffset
   \ht\nextbox\strutht
   \dp\nextbox\strutdp
   \shapingpenalty\plustenthousand
   \scratchwidth{\wd\nextbox+\scratchdistance}%
   \scratchhsize{\hsize-\scratchwidth}%
   \parshape {\noflines+\plusone}%
     \ifx\m_spac_hanging_location\v!right
       \expandedrepeat \noflines {\zeropoint \scratchhsize}
     \else
       \expandedrepeat \noflines {\scratchwidth \scratchhsize}
     \fi
     \zeropoint \hsize
   \relax
   \pushparagraphtweak {shift}
   \dontleavehmode
   \ifx\m_spac_hanging_location\v!right
     \rlap{\kern\scratchhsize\kern\scratchdistance\box\nextbox}%
   \else
     \llap{\box\nextbox\kern\scratchdistance}%
   \fi
   \ignorepars}

\permanent\protected\def\stophanging
  {\par
   \popparagraphtweak
   \endgroup
   \par}

%D \macros
%D   {startfixed}
%D
%D \starttyping
%D \startitemize
%D     \startitem \externalfigure[cow][height=1cm] \stopitem
%D     \startitem \externalfigure[cow][height=1cm] \stopitem
%D
%D     \startitem \startfixed      \externalfigure[cow][height=1cm]\stopfixed \stopitem
%D     \startitem \startfixed[high]\externalfigure[cow][height=1cm]\stopfixed \stopitem
%D     \startitem \startfixed[low] \externalfigure[cow][height=1cm]\stopfixed \stopitem
%D     \startitem \startfixed[lohi]\externalfigure[cow][height=1cm]\stopfixed \stopitem
%D
%D     \startitem test \par \startfixed      \externalfigure[koe][height=1cm]\stopfixed \stopitem
%D     \startitem test \par \startfixed[high]\externalfigure[koe][height=1cm]\stopfixed \stopitem
%D     \startitem test \par \startfixed[low] \externalfigure[koe][height=1cm]\stopfixed \stopitem
%D     \startitem test \par \startfixed[lohi]\externalfigure[koe][height=1cm]\stopfixed \stopitem
%D \stopitemize
%D \stopbuffer
%D
%D \typebuffer \getbuffer

\installcorenamespace{fixedalternatives}

\permanent\protected\lettonothing\stopfixed

\permanent\protected\def\startfixed
  {\bgroup
   \ifhmode
     \expandafter\typo_fixed_start_h
   \else
     \expandafter\typo_fixed_start_v
   \fi}

\tolerant\def\typo_fixed_start_h[#1]%
  {\enforced\let\stopfixed\typo_fixed_stop_h
   \dowithnextbox{\typo_fixed_finish{#1}}%
   \vbox\bgroup
   %ignorespaces
   \setlocalhsize}

\protected\def\typo_fixed_stop_h
  {%removeunwantedspaces
   \egroup
   \egroup}

\tolerant\def\typo_fixed_start_v[#1]%
  {\enforced\let\stopfixed\typo_fixed_stop_v
   \startbaselinecorrection}

\protected\def\typo_fixed_stop_v
  {\stopbaselinecorrection
   \egroup}

\letcsname\??fixedalternatives   \v!high\endcsname\bbox
\letcsname\??fixedalternatives    \v!low\endcsname\tbox
\letcsname\??fixedalternatives \v!middle\endcsname\vcenter
\letcsname\??fixedalternatives   \v!lohi\endcsname\vcenter
\letcsname\??fixedalternatives\s!unknown\endcsname\tbox
\letcsname\??fixedalternatives\s!default\endcsname\tbox

\protected\def\typo_fixed_finish#1%
  {\expandnamespacevalue\??fixedalternatives{#1}\s!default{\box\nextbox}}

% %D Forgotten already:
%
% \def\shapefill{\vskip\zeropoint\s!plus\lineheight\s!minus\lineheight\relax}

%D Nasty:

% \writestatus{1}{\the\prevdepth} \blank[force,5*big] { \writestatus{1}{\the\prevdepth} \baselineskip1cm xxxxxxxxx \par } \page
% \writestatus{2}{\the\prevdepth} \blank[force,5*big] { \writestatus{2}{\the\prevdepth} \baselineskip1cm xxxxxxxxx \par } \page
% \writestatus{3}{\the\prevdepth} \blank[force,5*big] { \writestatus{3}{\the\prevdepth} \baselineskip5cm xxxxxxxxx \par } \page
% \writestatus{4}{\the\prevdepth} \input tufte                                                                            \page
% \writestatus{5}{\the\prevdepth} \input tufte                                                                            \page
% \writestatus{6}{\the\prevdepth} \blank[force,5*big] { \writestatus{6}{\the\prevdepth} \baselineskip1cm xxxxxxxxx \par } \page

% \writestatus{1}{\the\prevdepth} \null\vskip4cm      { \writestatus{1}{\the\prevdepth} \baselineskip1cm xxxxxxxxx \par } \page
% \writestatus{2}{\the\prevdepth} \null\vskip4cm      { \writestatus{2}{\the\prevdepth} \baselineskip1cm xxxxxxxxx \par } \page
% \writestatus{3}{\the\prevdepth} \null\vskip4cm      { \writestatus{3}{\the\prevdepth} \baselineskip5cm xxxxxxxxx \par } \page
% \writestatus{4}{\the\prevdepth} \input tufte                                                                            \page
% \writestatus{5}{\the\prevdepth} \input tufte                                                                            \page
% \writestatus{6}{\the\prevdepth} \null\vskip4cm      { \writestatus{6}{\the\prevdepth} \baselineskip1cm xxxxxxxxx \par } \page

\appendtoks
    \ifvmode\prevdepth\zeropoint\fi % consistent, else first page -1000pt .. needed for fixed,3*big first/successive pages consistency
\to \everystarttext

\prevdepth\zeropoint

%D Helper:

\permanent\protected\def\checkedblank[#1]%
  {\edef\p_blank{#1}%
   \ifempty\p_blank
     % ignore
   \orelse\ifx\p_blank\v!none
     % ignore
   \else
     \blank[\p_blank]%
   \fi}

% \setupwhitespace[line]
% \prerollblank[2*line]  \the\prerolledblank
% \prerollblank[-2*line] \the\prerolledblank

\newgluespec\prerolledblank

\permanent\protected\def\prerollblank[#1]%
  {\begingroup
   \edef\p_blank{#1}%
   \ifempty\p_blank
     \global\prerolledblank\zeropoint
   \orelse\ifx\p_blank\v!none
     \global\prerolledblank\zeropoint
   \else
     % don't mess with \arskip here!
     \scratchskip\plusten\lineheight
     \setbox\scratchbox\vbox
       {\vskip\scratchskip
        \kern\zeropoint
        \blank[\p_blank]}%
     % \dimexpr doesn't work well with skips
     \advanceby\scratchskip-\ht\scratchbox
     \global\prerolledblank-\scratchskip
   \fi
   \endgroup}

% \setupwhitespace[line]
% \setuphead[subject][after={\blank[packed]},style=\bfb]
% \subject{foo}
% test \par
% test \par
% \blank[packed] % \ignoreparskip
% test \par
% test \par
% \ignoreparskip
% test \par
% test \par
% \setuphead[subject][after={\blank[nowhite]},style=\bfb]
% \subject{foo}
% test \par
% test \par

\permanent\protected\def\ignoreparskip{\c_spac_vspacing_ignore_parskip\plusone}

%D New, use with care:
%D
%D \starttyping
%D test test test \hfill\break
%D test \blankbefore test
%D test \blankbefore[2*line] test
%D test \blankbefore test
%D test test test \hfill\break
%D test \blankbefore test
%D test \blankbefore test
%D test \blankbefore test
%D test test test \hfill\break
%D test \blankafter test
%D test \blankafter test
%D test \blankafter test
%D test test test \hfill\break
%D test test test \hfill\break
%D \stoptyping

\permanent\tolerant\protected\def\blankbefore[#1]{\vadjust pre {\ifempty{#1}\blank\else\blank[#1]\fi}}
\permanent\tolerant\protected\def\blankafter [#1]{\vadjust post{\ifempty{#1}\blank\else\blank[#1]\fi}}

%D Experimental:

\installcorenamespace{textdisplay}

\installcommandhandler \??textdisplay {textdisplay} \??textdisplay

\appendtoks
    \frozen\protected\edefcsname\e!start\currenttextdisplay\endcsname{\spac_textdisplay_start{\currenttextdisplay}}%
    \frozen\protected\edefcsname\e!stop \currenttextdisplay\endcsname{\spac_textdisplay_stop}%
\to \everydefinetextdisplay

\setuptextdisplay
  [\c!factor=.5,
   \c!after=\textdisplayparameter\c!before]

\tolerant\protected\def\spac_textdisplay_start#1#:[#S#2]%
  {\par
   \begingroup
   \whitespace
   \forgetall
   \edef\currenttextdisplay{#1}%
   \setupcurrenttextdisplay[#2]%
   \textdisplayparameter\c!before
   \dostarttagged\t!textdisplay\currenttextdisplay
   \dpack
   \bgroup
   \strut
   \vskip-\lineheight
   \vpack
   \s!yoffset -\textdisplayparameter\c!factor\strutdp
   \bgroup}

\protected\def\spac_textdisplay_stop
  {\egroup
   \vskip-\lineheight
   \strut
   \egroup
   \dostoptagged
   \textdisplayparameter\c!after
   \endgroup
   \par}

\definetextdisplay[\v!textdisplay]

% \useMPlibrary[dum]
%
% \definetextdisplay[steppedbystep][before={\blank[halfline]}]
%
% \starttext \showmakeup[line]
%   \dostepwiserecurse{10}{25}{1}{
%       xxxxxxxxxxxxxxxx\par
%       \starttextdisplay
%         \externalfigure[dummydummy][height=#1mm,text=no]%
%       \stoptextdisplay
%       xxxxxxxxxxxxxxxx\par
%       \starttextdisplay[before=\blank,after=\blank] % factor=.5
%         \externalfigure[dummydummy][height=#1mm,text=no]%
%       \stoptextdisplay
%       xxxxxxxxxxxxxxxx\par
%       \startsteppedbystep
%         \externalfigure[dummydummy][height=#1mm,text=no]%
%       \stopsteppedbystep
%       xxxxxxxxxxxxxxxx\par
%       \page
%   }
% \stoptext

%D New:

% \doublepenaltymode\zerocount

\appendtoks
    % realpageno is now advanced
    \global\doublepenaltymode\rightorleftpageaction\zerocount\plusone
\to \everyaftershipout

% Here:

% not \break == -10000 because empty line (maybe we need somthing engine)

\permanent\protected\def\parbreak
  {\ifhmode\hfill\penalty-9999\relax\fi}

%D New feature (for finetuning formulas in columnsets):
%D
%D \starttabulate[|Tr|l|]
%D \NC [-]  T \NC minus  two     lines \NC \NR
%D \NC [-]  L \NC minus  one     line  \NC \NR
%D \NC [-]  H \NC minus  half    line  \NC \NR
%D \NC [-]  Q \NC minus  quarter line  \NC \NR
%D \NC +    T \NC plus   two     lines \NC \NR
%D \NC +    L \NC plus   one     line  \NC \NR
%D \NC +    H \NC plus   half    line  \NC \NR
%D \NC +    Q \NC plus   quarter line  \NC \NR
%D \NC [+-] Z \NC zero   ht/dp         \NC \NR
%D \NC [+-] I \NC ignore entry         \NC \NR
%D \stoptabulate

\newinteger\c_spac_snap_range

\permanent\tolerant\protected\def\setgridcorrection[#1]%
  {\ifempty{#1}%
     \attribute\snaprangeattribute\attributeunsetvalue
   \orelse\ifcstok{#1}\v!reset
     \attribute\snaprangeattribute\attributeunsetvalue
   \else
     \global\advanceby\c_spac_snap_range\plusone
     \clf_registersnaprange\c_spac_snap_range{#1}%
     \attribute\snaprangeattribute\c_spac_snap_range
   \fi}

\tolerant\permanent\protected\def\startgridcorrection
  {\begingroup
   \setgridcorrection}

\permanent\protected\def\stopgridcorrection
  {\endgroup}

\protect \endinput
