I have been trying to align text after \dotfill such that both (text) in the first line and (long text) in the second line are left aligned such that white space will be after the word (text) in the first line.
- 1,816
- 55
- 4
3 Answers
The idea is to split the input in chunks in order to be able to measure the relevant part.
\documentclass{article}
\ExplSyntaxOn
\NewDocumentEnvironment{dotfillenum}{+b}
{
\faizlotfy_dotfillenum:n { #1 }
}
{}
\seq_new:N \l__faizlotfy_dotfillenum_items_seq
\seq_new:N \l__faizlotfy_dotfillenum_row_seq
\dim_new:N \l__faizlotfy_dotfillenum_last_dim
\box_new:N \l__faizlotfy_dotfillenum_last_box
\cs_new_protected:Nn \faizlotfy_dotfillenum:n
{
% split the input at \item
\seq_set_split:Nnn \l__faizlotfy_dotfillenum_items_seq { \item } { #1 }
% remove the first (empty) item
\seq_pop_left:NN \l__faizlotfy_dotfillenum_items_seq \l_tmpa_tl
% start measuring the last elements
\dim_zero:N \l__faizlotfy_dotfillenum_last_dim
\seq_map_function:NN \l__faizlotfy_dotfillenum_items_seq __faizlotfy_dotfillenum_measure:n
% typeset
\begin{enumerate}
\seq_map_function:NN \l__faizlotfy_dotfillenum_items_seq __faizlotfy_dotfillenum_typeset:n
\end{enumerate}
}
\cs_new_protected:Nn __faizlotfy_dotfillenum_measure:n
{
\seq_set_split:Nnn \l__faizlotfy_dotfillenum_row_seq { & } { #1 }
\hbox_set:Nn \l__faizlotfy_dotfillenum_last_box
{
\seq_item:Nn \l__faizlotfy_dotfillenum_row_seq { 2 }\unskip
}
\dim_set:Nn \l__faizlotfy_dotfillenum_last_dim
{
\dim_max:nn { \l__faizlotfy_dotfillenum_last_dim }
{ \box_wd:N \l__faizlotfy_dotfillenum_last_box }
}
}
\cs_new_protected:Nn __faizlotfy_dotfillenum_typeset:n
{
\seq_set_split:Nnn \l__faizlotfy_dotfillenum_row_seq { & } { #1 }
\item
\seq_item:Nn \l__faizlotfy_dotfillenum_row_seq { 1 }
\dotfill
\makebox[\l__faizlotfy_dotfillenum_last_dim][l]
{
\seq_item:Nn \l__faizlotfy_dotfillenum_row_seq { 2 }
}
}
\ExplSyntaxOff
\begin{document}
words for context words for context words for context
words for context words for context words for context
words for context words for context words for context
\begin{dotfillenum}
\item $AB=CD$ & text;
\item $AB=CD$ & longer text;
\item $x$ & text.
\end{dotfillenum}
words for context words for context words for context
words for context words for context words for context
words for context words for context words for context
\begin{dotfillenum}
\item $AB=CD$ & text;
\item $AB=CD$ & longer text;
\item $x$ & text.
\end{dotfillenum}
\end{document}
The second example shows that blank lines in the environment are not taken into account.
- 1,121,712
Create an alignment where the text after \dotfill appears in a left-aligned column of its own. The tricky part is giving the column with the \dotfill the right width:
\halign to\hsize cannot be used, because this achieves the desired width by enlarging the \tabskip between columns, and this is filled with blank space, not with dots.
Instead, the \multispan3 statement is used, which ensures that all columns together use the full page width (\hsize), but TeX's alignment algorithm always puts the excess width into the last column. Therefore the alignment preamble has the column with the \dotfill as the last column, and this would produce the following output:
In order to swap the second and third columns, this alignment is typeset in a \vbox and then unpacked by the macro \swapcols (a similar idea has been used here).
\def\swapcols{\unskip\setbox0=\lastbox
\ifhbox0{\swapcols}\hbox{\unhbox0\swapcol}\fi}
\def\swapcol{\unskip\setbox2=\lastbox
\unskip\setbox1=\lastbox
\unskip\setbox0=\lastbox
\unskip
\box0\box2\box1}
\vbox{\halign{\hfil#\enspace&#\hfil&#\dotfill\cr
\multispan3{\hskip\hsize}\cr
1.&text;&$AB=CD$\cr
2.&longer text;&$AB=CD$\cr}\swapcols}
You can also embed this technique in an enumerate group. Then you need only two columns, but the first \multispan2 row must be skipped, lest it contribute an unwanted empty item:
\newif\ifskipped
\def\swapcols{\unskip\setbox0=\lastbox
\ifhbox0{\swapcols}\ifskipped\item\unhbox0\swapcol
\else\global\skippedtrue\fi\fi}
\def\swapcol{\unskip\setbox1=\lastbox
\unskip\setbox0=\lastbox
\unskip
\box1\box0}
\begin{enumerate}
\vbox{\halign{#\hfil&#\dotfill\cr
\multispan2{\hskip\hsize\hskip-\parindent}\cr %-\parindent compensates for the item number
text;&$AB=CD$\cr
longer text;&$AB=CD$\cr}\global\skippedfalse\swapcols}
\end{enumerate}
- 1,816
Leave this to eqparbox's \eqmakebox[<tag>][<align>]{<stuff>} which will extract the maximum width of all <stuff> under the same <tag>. You can then <align> the elements internally as desired (left, center/default or right).
\documentclass{article}
\usepackage{eqparbox,lipsum}
\begin{document}
\lipsum[1][1-5]
\begin{enumerate}
\item $AB = C$ \dotfill \eqmakebox[RHS][l]{text;}
\item $D = EF$ \dotfill \eqmakebox[RHS][l]{longer text;}
\end{enumerate}
\lipsum[2]
\end{document}
The widths are calculated and stored in the .aux and therefore requires at least two compilations with any change in the maximum width of <stuff> for any <tag>. This also goes for the first iteration of compiling (since there is no maximum width established yet.
- 603,163


