User:Dcwilson/DPalign

From DPWiki
Jump to navigation Jump to search

Dealing with Condensed Intertext in Formatting and Post-Processing

Older maths books sometimes run commentary inline with displays, like "Then" and "and" in this example:

Aligned-equation-dcwilson.png

This can be difficult to reproduce—the LaTeX tips page describes one slightly heavy-handed method using the amsmath environment flalign* with dummy alignment tabs and phantom text for balancing. Here I give an outline of a less intrusive (that is, more easily adapted by the PPer) way to get the same result.

Basically we use a new environment (built on top of amsmath environments) DPalign*. The preamble code to define the environment appears below (it assumes your preamble already includes \usepackage{amsmath}).

The run-in intertext can be positioned at the left margin using \lintertext{text}. There is also \rintertext (its main use is for manually balancing the centering of the alignment).

The environment has an optional argument: by default each \lintertext is balanced by a phantom \rintertext to keep the alignment centered without regard to the intertext. If this isn't appropriate an argument of [m] will suppress the automatic phantoms. Beware: if the first character inside your environment is a "[" then the macro will read this as the start of the optional argument, so any leading "[" needs to be hidden behind something innocuous like an empty pair of braces--"{}[".

To typeset the example above we can use

Join $PA$, $A'P$, and let these lines produced meet the directrix in $E$ and $F$.

\begin{DPalign*}
\lintertext{\indent Then} PN:AN &:: EX:AX,\\
\lintertext{and}         PN:A'N &:: FX:A'X;\\
         \therefore~PN^2:AN.NA' &:: EX.FX:AX.A'X\\
                                &:: SX^2:AX.A'X,
\end{DPalign*}
since $ESF$ is a right angle (Prop.\ 1.); that is, $PN^2$ is to $AN.NA'$ in a constant ratio.

which yields

Typeset1-dcwilson.png

If the PPer dislikes condensed intertext, it is very little work to change DPalign* to align* and lintertext to intertext, yielding a working align* environment in very short order. (In this example, the PPer would perhaps also move the "Then" outside the environment.) Undoing an flalign* is considerably more work.

Occasionally a book will also use condensed intertext with gathered equations: for this we have the DPgather* environment. To reproduce

Gathered-equation-dcwilson.png

we can use

...since $PM$ is bisected in $V$;
\begin{DPgather*}
\lintertext{also} TF:TM :: OE:OM;\\
       \therefore~OE^2=OA.OB.
\end{DPgather*}

Again, it is relatively straightforward to unwind this to a standard gather* if the PPer prefers.

Known problems

The environments will not always produce horizontal spacing of the equations identical to that of an ordinary align* or gather* environment. This is particularly noticeable when there are \tags involved. For formatting this doesn't really matter, but for post-processing it may necessitate the use of the [m] manual override parameter and some fiddling with spacing commands.

The way the environments are constructed means that the horizontal alignment assumes the worst case (the longest l/rintertext and the longest equation being on the same line). In some situations (such as where the longest intertext is against the shortest equation and vice versa) this can lead to unacceptable output. Formatters should not concern themselves with this, because the important thing is that the structure of the text has been faithfully captured. The appearance of the output is the domain of the PPer, and they may need recourse to width-concealing commands like \llap and \rlap in order to generate acceptable horizontal alignment.

Preamble code

\makeatletter
\providecommand\shortintertext\intertext
\newcount\DP@lign@no
\newtoks\DP@lignb@dy
\newif\ifDP@cr
\newif\ifbr@ce
\def\f@@zl@bar{\null} 
\def\addto@DPbody#1{\global\DP@lignb@dy\@xp{\the\DP@lignb@dy#1}}
\def\parseb@dy#1{\ifx\f@@zl@bar#1\f@@zl@bar
    \addto@DPbody{{}}\let\@next\parseb@dy
  \else\ifx\end#1%
    \let\@next\process@DPb@dy
    \ifDP@cr\else\addto@DPbody{\DPh@@kr&\DP@rint}\@xp\addto@DPbody\@xp{\@xp{\the\DP@lign@no}&}\fi
    \addto@DPbody{\end}%
  \else\ifx\intertext#1%
    \def\@next{\eat@command0}%
  \else\ifx\shortintertext#1%
    \def\@next{\eat@command1}%
  \else\ifDP@cr\addto@DPbody{&\DP@lint}\@xp\addto@DPbody\@xp{\@xp{\the\DP@lign@no}&\DPh@@kl}%
          \DP@crfalse\fi
    \ifx\begin#1\def\begin@stack{b}%
      \let\@next\eat@environment
  \else\ifx\lintertext#1%
    \let\@next\linter@text
  \else\ifx\rintertext#1%
    \let\@next\rinter@text
  \else\ifx\\#1%
    \addto@DPbody{\DPh@@kr&\DP@rint}\@xp\addto@DPbody\@xp{\@xp{\the\DP@lign@no}&\\}\DP@crtrue
    \global\advance\DP@lign@no\@ne
    \let\@next\parse@cr
  \else\check@braces#1!Q!Q!Q!\ifbr@ce\addto@DPbody{{#1}}\else
    \addto@DPbody{#1}\fi
    \let\@next\parseb@dy
  \fi\fi\fi\fi\fi\fi\fi\fi\@next}
\def\process@DPb@dy{\let\lintertext\@gobble\let\rintertext\@gobble
  \@xp\start@align\@xp\tw@\@xp\st@rredtrue\@xp\m@ne\the\DP@lignb@dy}
\def\linter@text#1{\@xp\DPlint\@xp{\the\DP@lign@no}{#1}\parseb@dy}
\def\rinter@text#1{\@xp\DPrint\@xp{\the\DP@lign@no}{#1}\parseb@dy}
\def\DPlint#1#2{\@xp\def\csname DP@lint:#1\endcsname{\text{#2}}}
\def\DPrint#1#2{\@xp\def\csname DP@rint:#1\endcsname{\text{#2}}}
\def\DP@lint#1{\ifbalancedlrint\@xp\ifx\csname DP@lint:#1\endcsname\relax\phantom
  {\csname DP@rint:#1\endcsname}\else\csname DP@lint:#1\endcsname\fi
  \else\csname DP@lint:#1\endcsname\fi}
\def\DP@rint#1{\ifbalancedlrint\@xp\ifx\csname DP@rint:#1\endcsname\relax\phantom
  {\csname DP@lint:#1\endcsname}\else\csname DP@rint:#1\endcsname\fi
  \else\csname DP@rint:#1\endcsname\fi}
\def\eat@command#1#2{\ifcase#1\addto@DPbody{\intertext{#2}}\or
  \addto@DPbody{\shortintertext{#2}}\fi\DP@crtrue
  \global\advance\DP@lign@no\@ne\parseb@dy}
\def\parse@cr{\new@ifnextchar*{\parse@crst}{\parse@crst{}}}
\def\parse@crst#1{\addto@DPbody{#1}\new@ifnextchar[{\parse@crb}{\parseb@dy}}
\def\parse@crb[#1]{\addto@DPbody{[#1]}\parseb@dy}
\def\check@braces#1#2!Q!Q!Q!{\def\dp@lignt@stm@cro{#2}\ifx
  \empty\dp@lignt@stm@cro\br@cefalse\else\br@cetrue\fi}
\def\eat@environment#1{\addto@DPbody{\begin{#1}}\begingroup
  \def\@currenvir{#1}\let\@next\digest@env\@next}
\def\digest@env#1\end#2{%
  \edef\begin@stack{\push@begins#1\begin\end \@xp\@gobble\begin@stack}%
  \ifx\@empty\begin@stack
    \@checkend{#2}
    \endgroup\let\@next\parseb@dy\fi
    \addto@DPbody{#1\end{#2}}
    \@next}
\def\lintertext{lint}\def\rintertext{rint}
\newif\ifbalancedlrint
\let\DPh@@kl\empty\let\DPh@@kr\empty
\def\DPg@therl{&\omit\hfil$\displaystyle}
\def\DPg@therr{$\hfil}
  
\newenvironment{DPalign*}[1][a]{%
  \if m#1\balancedlrintfalse\else\balancedlrinttrue\fi
  \global\DP@lign@no\z@\DP@crfalse
  \DP@lignb@dy{&\DP@lint0&}\parseb@dy
}{%
  \endalign
}
\newenvironment{DPgather*}[1][a]{%
  \if m#1\balancedlrintfalse\else\balancedlrinttrue\fi
  \global\DP@lign@no\z@\DP@crfalse
  \let\DPh@@kl\DPg@therl
  \let\DPh@@kr\DPg@therr
  \DP@lignb@dy{&\DP@lint0&\DPh@@kl}\parseb@dy
}{%
  \endalign
}
\makeatother