1

I want to write a simple glossary, using something like the code below.

\documentclass{article}

\usepackage{multicol}

\newcommand{\itemlex}[2]{%
    \mbox{#1} \dotfill{} \mbox{\itshape #2}%
\par}

\setlength{\parindent}{0pt}
\setlength{\parskip}{3pt plus 2pt}

\begin{document}

\begin{multicols}{2}
    \itemlex{un mot court}{a short word}
    \itemlex{un mot beaucoup plus long}{a much longer word}
    \itemlex{lorem}{lorem}
    \itemlex{ipsum}{ipsum}
    \itemlex{dolor}{dolor}
    \itemlex{sit}{sit}
    \itemlex{amet}{amet}
\end{multicols}

\end{document}

The result is as follows:

result

Is there a way to force \dotfill to span over the line break when it is 'necessary', ie. something like the following?

enter image description here

(This was produced manually with a \\ and a second \dotfill, I would like to have it automated when needed.)

3 Answers 3

4

The idea of my code is to create dots from two \dotfils. First one (with only one el) is defined by \hfil and the second one is the standard Plain-TeX \dotfill defined by \hfill. When your \itemlex prints only single line then the first \dotfil gets zero space and the second \dotfill is stretched. When your \itemlex needs two lines, then the line is broken at \penalty0, so first line is filled by the first \dotfil and the second line uses second \dotfill. Because the second \dotfill is created by \hfill (two el's) then the standard setting of the \parfillskip register (i.e. 1fil) creates zero space at the end of the paragraph.

Note that the second \dotfill is "protected" by \null because the \dotfill is discaradable item (it is deleted if line is broken immediately before it). And it is "protected" by \nobreak because we don't want to break the line at \dotfill itself.

\def\itemlex#1#2{%
   \noindent \hbox{#1} \dotfil\penalty0 \null\nobreak\dotfill ~\hbox{#2}\par
}
\def\dotfil{\cleaders\hbox{$\mkern1.5mu.\mkern1.5mu$}\hfil}

\hsize=7cm

   \itemlex{un mot court}{a short word}
   \itemlex{un mot beaucoup plus long}{a much longer word}
   \itemlex{lorem}{lorem}

\bye
3
  • Thanks for your answer! When a leader is broken in two lines, spaces appear at the end of the 1st line and at the beginning of the 2nd one, do you know how to remove them (so that the first dot is aligned with the border of the paragraph)? Aug 29, 2022 at 9:21
  • This is no space, but it is feature of the \cleaders primitive. There can't be more dots here because the line would be overflowed. The maximum number of dots is used and the spaces left and right around the \cleaders is used. And don't forget that each dot is in a box with little spaces left and right around the dot (see \mkern1.5mu in the definition of \dotfill). You can replace \cleaders by \xleaders (re-define \dotfil and \dotfill macros): then the space will be not at at left and right of \dotfill but it is decomposed between each dots.
    – wipet
    Aug 29, 2022 at 20:00
  • @UlrichDiez My TeXbook naruby petr.olsak.net/ftp/olsak/tbn/tbn.pdf says the same on page 117 below including illustrative picture. I don't understand why you cited TeXbook for me. IMHO, OP claimed "spaces" around \cleaders, not the fact that the dots are not aligned. I suggested to use \xleaders because the "spaces" around \xleadders are significantly smaller. If we use \leaders then there is height probability that the "space" at the end of the line is much bigger. It can be solved if we select \hsize as an integer multiple of the width of the \leaders box.
    – wipet
    Aug 30, 2022 at 5:30
4

The idea of "leaders" is to line up as many "leader-boxes" as fit into the gap. The total size of the leader-boxes that fit in the gap can be slightly smaller than the gap itself.

\leaders packs leader-boxes tightly next to each other, starting from the edge of the surrounding box, but showing only those leader-boxes that are entirely within the space occupied by the gap. In this way it is possible, for example, to have elements of the same kind of horizontal leaders in different lines within the same vertical box to be vertically flush with each other.

\cleaders packs as many leader-boxes tightly next to each other as fit into the gap and centers this construct in the gap.

\xleaders packs as many leader-boxes as fit into the gap and inserts glue of the same size before the first leader-box and between all these leader-boxes and after the last leader-box in such a way that the construct of glue and leader-boxes has the same dimensions as the gap.

If you use \leaders, so that leader-boxes of different lines (columns if vertical leaders) flush with each other, for at least one box with the leader-box appearing in the gap, the gap needs to be at least twice as large as the leader-box.

Wipet's idea of creating one glob of horizontal glue with leader-boxes with "\hfil" and another glob of horizontal glue with "\hfill" and inserting a penalty in between to "tell" TeX that wrapping/breaking is allowed here is the gist/the essence of the solution to the problem.

If there is no line break, the \hfill glue is much more "expansive" than the \hfil glue and takes up the remaining line width, while the \hfil glue is not given any space at all because of the much greater "expansiveness" of the \hfill glue, so it is not created at all. When a line break occurs, one glue fills the rest of the top line to the right and the other fills the bottom line from the left.

There is not much I can add to Wipet's approach. Except that I try to address the belated request in your comment to Wipet's response that the dots be flush left and flush right and also dots in different lines to be vertically flush with each other:

You tried with \dotfill. But \dotfill is not for situations where you wish the elements of horizontal leaders of different lines to be vertically flush with each other, firstly because \dotfill uses \cleaders, and secondly because the box to be set as leaders is a box of width .44em within which a dot is set horizontally centered. Because the width of the leader-box is fixed for \dotfill, the width of the gap between the dots is also fixed.

From what was said above about the different types of leaders:

  • Since the dots in different lines shall be vertically flush with each other, only \leaders comes into question. \cleaders and \xleaders are out of the question.
  • Since the dots are to be flushd both on the left and on the right edge of the surrounding vertical box,
    • The leader-box should not contain a single centered dot but a dot at the left, a gap, and another dot at the right whose width is not accounted to the width of the leader-box. The dot at the left ensures dot-leaders at the beginnings of lines are flushed to the left edge of the surrounding box. The dot at the right is overlapped by the dot at the left of the next leader-box, except with the last leader-box. This way, if the width of the leader-box is calculated as described in the following, dot-leaders at the end of a line of text are flushed to the right edge of the srurrounding box.

    • the width of the leader-box needs to be be set/calculated in dependence on

      • the line width (\hsize),
      • the desired minimum width of a leader-box,
      • the width of a dot

      as follows:

      ⟨width of leader‑box⟩ = ( ⟨linewidth⟩ − ⟨width of a dot⟩ ) / ( ( ⟨linewidth⟩ − ⟨width of a dot⟩ ) div ⟨desired minimum width of a leader‑box⟩ )

      I haven't given much thought about how to do calculate like this in TeX most accurately.
      But there is a length to be calculated. The smallest length-unit in TeX is scaled point (sp).

      With arithmetic operations where as tokens denoting ⟨number⟩-quantities count-registers are accepted, you can as well use dimension- or skip-registers as long as you don't use \the but let TeX do the work of obtaining the value of the dimension- or skip-register in question directly. If you do this, then TeX assumes the numerical value which belongs to the length quantity in question when it is normalized to scaled point.

      So when doing calculations you can treat lengths as normalized to scaled point by using dimension-registers/dimension-parameters like count-registers, avoiding applying \the to them.

      In case you are interested here is a conversion table for the length units understood by TeX:

      pt point (baselines in this manual are 12 pt apart)
      pc pica (1 pc = 12 pt)
      in inch (1 in = 72.27 pt)
      bp big point (72 bp = 1 in)
      cm centimeter (2.54 cm = 1 in)
      mm millimeter (10 mm = 1 cm)
      dd didot point (1157 dd = 1238 pt)
      cc cicero (1 cc = 12 dd)
      sp scaled point (65536 sp = 1 pt)

      The following \numexpr might do the trick of calculating the required width of the leader-box:

      \settowidth\dotwidth{.}%
      \setlength\minimumleaderboxwidth{.44em}%
      \setlength\leaderboxwidth{%
        \dimexpr
          \numexpr(\hsize-\dotwidth)/(%
            ((\hsize-\dotwidth)/\minimumleaderboxwidth)%
            \ifnum
              \numexpr((\hsize-\dotwidth)/\minimumleaderboxwidth)*(\hsize-\dotwidth)\relax>\numexpr\hsize-\dotwidth\relax
              -1%
            \fi
          )\relax
        sp\relax
      }%
      

In the following example a vertical box is created, with a grid, which shows the horizontal position of dots created by \leaders.

Another box is put over it, in which text with various leaders is created.

\documentclass{article}

\usepackage{multicol}

\usepackage{xcolor}

\newbox\textbox
\newbox\gridbox

\newlength\leaderboxwidth
\newlength\dotwidth
\newlength\minimumleaderboxwidth
\newcommand\calculateleaderparameters{%
  \settowidth\dotwidth{.}%
  \setlength\minimumleaderboxwidth{.44em}%
  \setlength\leaderboxwidth{%
    \dimexpr
      \numexpr(\hsize-\dotwidth)/(%
        ((\hsize-\dotwidth)/\minimumleaderboxwidth)%
        \ifnum
          \numexpr((\hsize-\dotwidth)/\minimumleaderboxwidth)*(\hsize-\dotwidth)\relax>\numexpr\hsize-\dotwidth\relax
          -1%
        \fi
      )\relax
    sp\relax
  }%
}%

\newcommand*\Dotfillleaders{\leaders\hbox to\leaderboxwidth{.\hss\rlap{.}}}
\newcommand*\mybreak{%
  \unskip
  \nobreak
  \Dotfillleaders\hskip2\leaderboxwidth\nobreak
  \Dotfillleaders\hfil
  \penalty 0 \hfilneg
  \null\nobreak
  \Dotfillleaders\hskip\the\dimexpr2\leaderboxwidth\relax plus 1fil\nobreak
  \null\nobreak\kern\dotwidth\nobreak
}


\csname @ifdefinable\endcsname\itemlex{%
  \def\itemlex#1#2\par{%
    {%
      % The command \calculateleaderparameters can be
      % moved into the multicols-environment instead of 
      % calculating the same things again and again with each instance 
      % of \itemlex. If you do that, \calculateleaderparameters
      % needs to be called within the multicols-environment right
      % after beginning it.
      \calculateleaderparameters
      \leavevmode#1\mybreak{\itshape#2\/}%
      {\setbox\scratchbox=\hbox{#2}%
      \ifdim\wd\scratchbox=0pt \kern-\dotwidth\fi}%
      \parfillskip=0pt \finalhyphendemerits=0 \par
    }%
  }%
}

\setlength{\parindent}{0pt}
\setlength{\parskip}{3pt plus 2pt}

\begin{document}

\setbox\textbox\vbox{%
\begin{multicols}{2}
    \itemlex{}{}\par
    \itemlex{un mot court}{a short word}\par
    \itemlex{un mot beaucoup plus long}{a much longer word}\par
    \itemlex{un mot beaucoup beaucoup plus plus long}{a much much much longer word}\par
    \itemlex{un mot beaucoup beaucoup plus long}{a much much much much longer word}\par
    \itemlex{un mot beaucoup beaucoup plus}{muchlo much much much longer word}\par
    \itemlex{un mot beaucoup beaucoup plus long}{a much much much longer word}\par
    \itemlex{un mot beaucoup beaucoup plus plus long}{mucho much much longer word word word}\par
    \itemlex{un mot beaucoup beaucoup plus plus long}{mucho much much longer word word word word}\par
    \itemlex{un mot beaucoup beaucoup plus plus long}{mucho much much longer word word word word mucho much much longer word word word word ybjj}\par
    \itemlex{lorem}{lorem}\par
    \itemlex{ipsum}{ipsum}\par
    \itemlex{dolor}{dolor}\par
    \itemlex{sit}{sit}\par
    \itemlex{amet}{amet}\par
    \itemlex{{\bfseries There will be at least four dots:}}{}\par
    \itemlex{test}{1234567890123456789012345}\par
    \itemlex{test}{12345678901234567890123456}\par
    \itemlex{1234567890123456789012}{test test}\par
    \itemlex{12345678901234567890123}{test test}\par
    \itemlex{1234567890123456789012}{test test test}\par
    \itemlex{}{}\par
    \itemlex{}{}\par
    \itemlex{}{}\par
\end{multicols}%
}%

\setbox\gridbox\vbox{%
  \begingroup
  \color[RGB]{242, 242, 242}%
  \begin{multicols}{2}%
  \calculateleaderparameters
  \leavevmode 
  \leaders
    \hbox to\leaderboxwidth{%
      \kern.5\dimexpr\dotwidth-.4pt\relax
      \vrule height\dimexpr\ht\textbox-2\multicolsep\relax
             depth \dimexpr\dp\textbox+\dp\strutbox\relax
      \hss
      \rlap{%
        \kern.5\dimexpr\dotwidth-.4pt\relax
        \vrule height\dimexpr\ht\textbox-2\multicolsep\relax
               depth \dimexpr\dp\textbox+\dp\strutbox\relax
      }%
    }%
  \hfill\null
  \par
  \leavevmode 
  \leaders
    \hbox to\leaderboxwidth{%
      \kern.5\dimexpr\dotwidth-.4pt\relax
      \vrule height\dimexpr\ht\textbox-2\multicolsep\relax
             depth \dimexpr\dp\textbox+\dp\strutbox\relax
      \hss
      \rlap{%
        \kern.5\dimexpr\dotwidth-.4pt\relax
        \vrule height\dimexpr\ht\textbox-2\multicolsep\relax
               depth \dimexpr\dp\textbox+\dp\strutbox\relax
      }%
    }%
  \hfill\null
  \par
  \end{multicols}
  \endgroup
}%

\leavevmode\box\gridbox\hskip-\wd\textbox\box\textbox

\end{document}

enter image description here

And here is an example where all the stuff for drawing the grid is omitted:

\documentclass{article}

\usepackage{multicol}

\newlength\leaderboxwidth
\newlength\dotwidth
\newlength\minimumleaderboxwidth
\newbox\scratchbox
\newcommand\calculateleaderparameters{%
  \settowidth\dotwidth{.}%
  \setlength\minimumleaderboxwidth{.44em}%
  \setlength\leaderboxwidth{%
    \dimexpr
      \numexpr(\hsize-\dotwidth)/(%
        ((\hsize-\dotwidth)/\minimumleaderboxwidth)%
        \ifnum
          \numexpr((\hsize-\dotwidth)/\minimumleaderboxwidth)*(\hsize-\dotwidth)\relax>\numexpr\hsize-\dotwidth\relax
          -1%
        \fi
      )\relax
    sp\relax
  }%
}%

\newcommand*\Dotfillleaders{\leaders\hbox to\leaderboxwidth{.\hss\rlap{.}}}
\newcommand*\mybreak{%
  \unskip
  \nobreak
  \Dotfillleaders\hskip2\leaderboxwidth\nobreak
  \Dotfillleaders\hfil
  \penalty 0 \hfilneg
  \null\nobreak
  \Dotfillleaders\hskip\the\dimexpr2\leaderboxwidth\relax plus 1fil\nobreak
  \null\nobreak\kern\dotwidth\nobreak
}


\csname @ifdefinable\endcsname\itemlex{%
  \def\itemlex#1#2\par{%
    {%
      % The command \calculateleaderparameters can be
      % moved into the multicols-environment instead of 
      % calculating the same things again and again with each instance 
      % of \itemlex. If you do that, \calculateleaderparameters
      % needs to be called within the multicols-environment right
      % after beginning it.
      \calculateleaderparameters
      \leavevmode#1\mybreak{\itshape#2\/}%
      {\setbox\scratchbox=\hbox{#2}%
      \ifdim\wd\scratchbox=0pt \kern-\dotwidth\fi}%
      \parfillskip=0pt \finalhyphendemerits=0 \par
    }%
  }%
}

\setlength{\parindent}{0pt}
\setlength{\parskip}{3pt plus 2pt}

\begin{document}

\begin{multicols}{2}
    \itemlex{}{}\par
    \itemlex{un mot court}{a short word}\par
    \itemlex{un mot beaucoup plus long}{a much longer word}\par
    \itemlex{un mot beaucoup beaucoup plus plus long}{a much much much longer word}\par
    \itemlex{un mot beaucoup beaucoup plus long}{a much much much much longer word}\par
    \itemlex{un mot beaucoup beaucoup plus}{muchlo much much much longer word}\par
    \itemlex{un mot beaucoup beaucoup plus long}{a much much much longer word}\par
    \itemlex{un mot beaucoup beaucoup plus plus long}{mucho much much longer word word word}\par
    \itemlex{un mot beaucoup beaucoup plus plus long}{mucho much much longer word word word word}\par
    \itemlex{un mot beaucoup beaucoup plus plus long}{mucho much much longer word word word word mucho much much longer word word word word ybjj}\par
    \itemlex{lorem}{lorem}\par
    \itemlex{ipsum}{ipsum}\par
    \itemlex{dolor}{dolor}\par
    \itemlex{sit}{sit}\par
    \itemlex{amet}{amet}\par
    \itemlex{{\bfseries There will be at least four dots:}}{}\par
    \itemlex{test}{1234567890123456789012345}\par
    \itemlex{test}{12345678901234567890123456}\par
    \itemlex{1234567890123456789012}{test test}\par
    \itemlex{12345678901234567890123}{test test}\par
    \itemlex{1234567890123456789012}{test test test}\par
    \itemlex{}{}\par
    \itemlex{}{}\par
    \itemlex{}{}\par
\end{multicols}%

\end{document}

enter image description here

3

You can use the code below. The idea is to add a shrinking space and an additional stretching 'dotfill' which would cancel each other when they are both on one line. Also, I inserted some penalties to make sure the line break occurs only between these two skips. This means that if the line is long then the first shrinking space stays before the linebreak (and is discarded by the TeX line breaking algorithm), and the second stretching 'dotfill' moves to the second line and shows itself. I had to set \parfillskip to zero to make sure there's no gap at the end of the last line. And I had to use finite shrinkage and/or stretching skips because TeX forbids to use infinite shrinkage in paragraphs. A small issue with this particular approach is undefull hbox which occurs at the beginning of the second line, but it's harmless.

\documentclass{article}

\usepackage{multicol}

\newcommand{\itemlex}[2]{%
  \parfillskip=0pt
  \mbox{#1} \dotfill\hskip0pt minus1pt{}\penalty10\hbox{}\nobreak\leaders\hbox to 0.44em{.}\hskip 0pt plus1pt \nobreak\mbox{\itshape #2}%
\par}

\setlength{\parindent}{0pt}
\setlength{\parskip}{3pt plus 2pt}

\begin{document}

\begin{multicols}{2}
    \itemlex{un mot court}{a short word}
    \itemlex{un mot beaucoup plus long}{a much longer word}
    \itemlex{lorem}{lorem}
    \itemlex{ipsum}{ipsum}
    \itemlex{dolor}{dolor}
    \itemlex{sit}{sit}
    \itemlex{amet}{amet}
\end{multicols}

\end{document}

The result: enter image description here

2
  • Thanks! There's still an issue with this solution, try for example \itemlex{un mot bea plus long}{a muc longer word}, the two texts overlap. Aug 29, 2022 at 9:17
  • Heh, you're right, if left and right boxes overlap by less than 1em the issue becomes apparent. I don't have a proper fix for that, so you can replace 1em by 1pt to mitigate the issue a bit. Aug 30, 2022 at 11:01

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .