TUGboat, Volume 39 (2018), No. 3

263

The dashundergaps package∗Frank Mittelbach
Abstract
The dashundergaps package offers the possibility to replace material in running text
with white space in order to build up forms that can be filled in at a later time.
By default the gaps are underlined and followed by a gap number in parentheses, butmany other designs are possible, e.g., dashes or dots instead of the underline, no gap
numbers or a different format for them, gap widening for easier fill-in, etc.
There is also a teacher’s mode which shows the normally hidden text in a special(customizable) format.
This is another article in a series of TUGboat articles describing small packages tointroduce coding practices using the expl3 programming language. See [1] for the firstarticle in the series. For more details on expl3 refer to [2].
Contents
1
2

3
4
5

1

Introduction
The user interface
2.1 Options to customize the gap display . . . . . . . . . . . . . . . . .
2.1.1 Gap modes . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.1.2 Gap formatting . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.1.3 Gap numbers . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.1.4 Gap widening . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Differences from the original package
Solution to the puzzle
The implementation
5.1 Loading and fixing/changing ulem . . . . . . . . . . . . . . . . . . .
5.2 The main implementation part . . . . . . . . . . . . . . . . . . . . .
5.2.1 User interface commands . . . . . . . . . . . . . . . . . . . . .
5.2.2 Counters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.2.3 Messages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.2.4 Option handling . . . . . . . . . . . . . . . . . . . . . . . . . .
5.2.5 Closing shop . . . . . . . . . . . . . . . . . . . . . . . . . . . .

263
264
265
265
265
266
266
266
267
268
268
269
269
269
270
270
274

Introduction

The dashundergaps package provides a single command \gap which takes one argument
and produces a gap of the width of that argument. To better mark this gap it isunderlined in some form (could be a solid line, a dashed or dotted line or even awriggling line). Furthermore, gaps can be numbered to be able to easily refer to them.
::::::::::::
Figure 1 shows an example in the form of a fill-in puzzle.
As you see there, some gaps are numbered with a superscript number (not the default
setting) while others aren’t. How this is done and how to change the result is explainedin the next section.
There also exists a “teacher mode” in which the gaps are filled with the text given in
the argument. This can be used to show the correct answers of a test (as we do in
Section 4) or to give a sample fill-in for a form, to help people fill it out correctly. The∗ This is a reimplementation (using expl3, the L
ATEX3 programming language) of a package
originally written by Luca Merciadri in 2010.

The dashundergaps package

264

TUGboat, Volume 39 (2018), No. 3

The initial ‘E.’ in Donald E. Knuth’s name stands for(1)

the Ultimate Question(2). The well-known answer tois 42 according to. The first edition of(3)celebrates its silver anniversary

in 2019. Historically speaking, expl3 stands for(4)even though it is a production language these days.

And here are some hints for the puzzle if you want to fill it out:
1. If only everything would be that easy
to answer.
2. The author of the book “Last Chance
To See” and of a famous radio show.

3. Back then known as the doggie book.
4. Old names die hard.

The answers are given in Section 4, showing the gaps filled in using the so-called teacher mode,
which can be activated or deactivated at any point in the document.
Figure 1: A fill-in puzzle using dashundergaps“teacher mode” produces the same line breaks because it ensures that the fill-ins takethe same amount of space as the gaps.
Another important feature is the possibility to artificially widen the gaps, compared tothe textual material in the argument. After all, when a form is filled by hand people
typically need more space to write some text compared to the same text being typeset.
So making the gaps simply as wide as the material likely results in too little space.
2

The user interface

The dashundergaps package is built as a small application on top of the ulem package,a package that defines several commands for underlining hsimple-texti in various ways.
\uline\uuline\uwave\dashuline\dotuline\gap\uline{hsimple-text i} \uwave{hsimple-text i} ...

This means that by loading dashundergaps the ulem commands such as \uline, \uwave
and so forth are automatically made available. These commands are used to do mostof the work and the current package only makes sure that, instead of the words, empty
boxes of the same width are used by ulem. This way we get underlined gaps of theright size.
By default, ulem changes \emph to underline text, so for this application, it is loadedwith the option normalem to prevent that from happening.
\gap*[hstyle i]{htext i}

The main command provided by the package is \gap which expects a mandatory htexti
argument containing the material that is used to produce the gap (and is normallyinvisible). By default the gap is underlined, though that can be changed.
u = \uline
d = \uuline
The optional hstylei argument explicitly defines a certain type of underlining: u stands
w = \uwave
for normal underlining (via \uline), d for double underlining (via \uuline), w for ab = hblanki
wavy line (via \uwave), b for blank (i.e., no underlining whatsoever), “-” for a dash-line- = \dashuline(via \dashuline) and finally “.” for underlining with dots (via \dotuline).
. = \dotuline
In the default configuration gaps are numbered using the counter gapnumber and thisnumber is shown in parentheses after the gap. With the star form the generation of thenumber is toggled, i.e., if it would be produced because of the current option settings

Possible hstyleis:Frank Mittelbach

TUGboat, Volume 39 (2018), No. 3

265

it will be suppressed; if it is suppressed through an option it will be typeset. This way
one can select the most convenient setting via an option for the whole document anduse * to toggle it as needed.
Since \gap uses ulem’s commands it inherits the limitations of these commands; notably,
only simple text can be used in the htexti argument. For example, a \footnote couldn’t
be used in the argument (but then that wouldn’t make much sense in a gap, would it?).
\TeacherModeOn\TeacherModeOff\TeacherModeOn % show gap material\TeacherModeOff % do not show gap material

Also supported is a teacher mode in which the material for the gaps is visible. This canbe used to show the expected answers in case \gap is used for preparing tests, or toshow a sample fill-in of a form. The teacher mode can be turned on or off anywhere in
the document using \TeacherModeOn or \TeacherModeOff, respectively. Alternatively,
it can also be set via an option, as we will see below.
\dashundergapssetup\dashundergapssetup{hcomma-separated key-value list i}

The package can be loaded with a number of options (discussed in Section 2.1). A
likely better approach is to set any options with the declaration \dashundergapssetup
which is normally used in the preamble, but can be used throughout the document tochange settings on the fly. It only changes explicitly given options so it can be used to
overwrite some defaults but leave everything else unchanged.
2.1

Options to customize the gap display

All of the package options are implemented as key/value options. For boolean options
one can give just the option name as a short form for setting the option to true.
Most options can be specified during package loading in the optional argument of\usepackage. However if the value requires some LATEX code (e.g., gap-font, which
expects a font declaration command) then this will not work due to some limitations in
the current LATEX package loader. For such options use \dashundergapssetup instead,which will always work.
2.1.1

Gap modes

The general processing mode is defined through the following options:teacher-mode Boolean that turns on teacher mode (i.e., the gap material will bevisible if set to true). Its default is false.
gap-mode Boolean that is the inverse of teacher-mode and just provided for convenience, i.e., an abbreviation for teacher-mode=false.
teachermode Alternative name for teacher-mode because that is what it was calledin the first package release.
2.1.2

Gap formatting

Formatting of the gaps is handled by the following six options:gap-format A choice option defining how the gap is marked. It accepts the following
values: underline (default), double-underline, dash, dot, wave, blank.
gap-format-adjust A boolean (default true). If set, the “line” below the gap israised to be roughly at the baseline, which normally looks better when there is notext above the line.
teacher-gap-format Another choice option, with the same values as gap-format,
used when we are in “teacher mode”, but this time the default is blank as normallythe gap text is typeset in the bold font and is therefore already identifiable, withThe dashundergaps package

266

TUGboat, Volume 39 (2018), No. 3
no need for additional underlining. However, depending on the circumstances it
might be helpful to keep the underlining (or use a different kind of underlining)
while in “teacher mode”.
gap-font This option expects a font directive as its value, e.g., \bfseries (whichis also the default). Using this option without supplying a value is equivalent
to supplying an empty value. It will be used to determine the font for the gap
material regardless of the mode. This is important to ensure that the gaps always
have the same width regardless of whether or not the material is shown.
For the example puzzle above it was set to \itshape, which you can see in the
puzzle answer.
dash Short name for gap-format=dash.
dot Short name for gap-format=dot.
2.1.3

Gap numbers

Producing the gap numbers is handled by the following options:gap-numbers Boolean that determines whether or not gap numbers are displayed.
Default is true.
gap-number-format Code that is executed when a gap number is produced. Default
is \textnormal{␣(\thegapnumber)}.
numbers Short name for gap-numbers.
There is also a way to control displaying the total number of gaps:
display-total-gaps Boolean to determine if the total number of gaps should beshown at the very end of the document. Default is false.
displaynbgaps This is just another name for the same boolean; it was used in thefirst version of the package.
2.1.4

Gap widening

Finally, for extending the gap width we have these options:gap-widen Boolean that decides if the gaps should be made wider or not (default isfalse but mainly for historical reasons).
gap-extend-minimum Minimum of extra space that should be added to each gap if
gap widening is active. Default is 20pt, i.e., 10pt on either side.
gap-extend-percent Percentage (as a number) by which the gap should be madewider if widening is active. The result is compared to gap-extend-minimum andthe larger of the two is used. Default is 20.
widen Short name for gap-widen.
3

Differences from the original package

The main user interface of the two versions is identical, so it is possible to use thenew version as a drop-in replacement for the old. However, the feature set in formof key/value options has been greatly extended, offering functionality previously
unavailable. Furthermore, a number of bugs have been corrected (and possibly newones introduced).
• Stray spaces in the definition of \gap (that showed up in the output) have been
eliminated.
• Various combinations of options that didn’t work are now possible.
• Explicit hyphenations \- showed up in gap mode, now they can be used.
• Nesting isn’t possible for obvious reasons, but the fact is now detected and catered
to by ignoring the inner gap requests after generating an error.
Frank Mittelbach

TUGboat, Volume 39 (2018), No. 3

267• Option names have been normalized (though the original names are still available).
• The option phantomtext is no longer necessary, though still supported (with awarning) as a no-op.
• The names of the LATEX counters used have changed, so if you directly addressed
them that would need changing.
• The font used in teacher mode (by default boldface) is now also used if gap modeis chosen, to ensure that the output in all modes produces identical line breaks;
for the same reason, the ulem machinery is always used, even if not underlining(or dashing, etc.).
• The gaps can be extended by a percentage or by a minimum amount to ensure that
there is enough space to fill in the text (given that hand-written text is typicallywider than typeset material); the values are adjustable.
• \gap now has an optional argument through which you can explicitly request thetype of underlining you want to use.
• \gap also supports a star form which toggles the setting of gap numbers.
• The use of \label within the \gap command argument allows for later reference
to that gap by its number (provided a gap number is typeset).
• The implementation is done with expl3, the programming language for LATEX3.
Although invisible to the user, in some sense that was the main purpose of theexercise: to see how easy it is to convert a package and use the extended featuresof expl3.
4

Solution to the puzzle

Here we repeat the puzzle from above with \TeacherModeOn.
The initial ‘E.’ in Donald E. Knuth’s name stands for Ervin (5) . The well-known answer tothe Ultimate Question
Adams

(6)

of Life, the Universe, and Everything. The first edition of

The LATEX Companion

in 2019. Historically speaking, expl3 stands for
(8)

(7)

is 42 according to Douglas
celebrates its silver anniversary

EXperimental Programming Language 3even though it is a production language these days.
This was produced using the following changes to the defaults:\dashundergapssetup{,gap-number-format= \,\textsuperscript{\normalfont(\thegapnumber)},gap-font= \itshape,teacher-gap-format = underline,gap-widen}
As you can see we use \itshape for the font (to be able to show the bold face in
one of the answers) and also force underlining in teacher mode to better show thegap widening. The gap number is raised and we separate it a tiny bit from the gap
material. We also use \normalfont in the formatting to ensure that the gap number
is set upright and not in italic shape.

The dashundergaps package

268

TUGboat, Volume 39 (2018), No. 3
5

The implementation

5.1

Loading and fixing/changing ulem

The first thing to do is to load ulem without changing \emph or \em:12

h*packagei\RequirePackage[normalem]{ulem}

The code in this section follows LATEX 2ε conventions, i.e., models the commands as
they look in the ulem package.
\dotuline

The dots produced by \dotuline depend on the current font, which is a somewhat
questionable design — if you underline a text part with a single bold word somewhere
inside it will change the shape of the dot line. So we always use the \normalfont dot(this is not done in the original definition).
\def\dotuline{\bgroup\UL@setULdepth
5\markoverwith{\begingroup6\advance\ULdepth0.08ex
7\lower\ULdepth\hbox{\normalfont \kern.1em .\kern.04em}%
8\endgroup}%
9\ULon}
10 \MakeRobust\dotuline
3
4(End definition for \dotuline. This function is documented on page 264.)\uwaveThe original \uwave used a hard-wired value of 3.5pt for the lowering. We changethat to be based on the current value of \ULdepth so that the user (or this packagehere) can change the placement.
\def\uwave{\bgroup\UL@setULdepth
13\advance\ULdepth 0.6\p@14\markoverwith{\lower\ULdepth\hbox{\sixly \char58}}\ULon}
15 \MakeRobust\uwave
11

12(End definition for \uwave. This function is documented on page 264.)\fmdug@ublank\fmdug@ublank underlines with blanks. Normally not especially useful (which is why
we make it internal), but if we want to have ulem acting, but without actually visibly
underlining, this is the command to use.
16

\def\fmdug@ublank{\bgroup\let\UL@leadtype\@empty\ULon}(End definition for \fmdug@ublank.)\UL@dischyp\UL@putbox

We need to do a little patching to ensure that nothing is output by the ulem commands
if we don’t want it to. So the next two commands are from ulem with \box replacedby \fmdug@box so that we can change the behavior.
\def\UL@dischyp{\global\setbox\UL@hyphenbox\hbox{\ifnum \hyphenchar\font<\z@ \string-\else \char\hyphenchar\font \fi}%19\kern\wd\UL@hyphenbox \LA@penalty\@M
20\UL@stop \kern-\wd\UL@hyphenbox
21\discretionary{\fmdug@box\UL@hyphenbox}{}{}\UL@start}
17
18\def\UL@putbox{\ifx\UL@start\@empty \else % not inner\vrule\@width\z@ \LA@penalty\@M
24{\UL@skip\wd\UL@box \UL@leaders \kern-\UL@skip}%
25\fmdug@box\UL@box \fi}22
23

(End definition for \UL@dischyp and \UL@putbox.)

Frank Mittelbach

TUGboat, Volume 39 (2018), No. 3\fmdug@box

269

By default we output the box in the commands above, but when we don’t want to
output anything visible we change the definition to generate a box with empty content
but the right size.
26\let\fmdug@box\box(End definition for \fmdug@box.)

5.2

The main implementation partThe rest of the package is written in expl3. We use fmdug as our internal prefix.
27

h@@=fmdugi

We need the package xparse for specifying the document-level interface commands
and l3keys2e to use the expl3 key value methods within LATEX 2ε . These packages
automatically require expl3 so there is no need to load that explicitly.
28\RequirePackage{xparse,l3keys2e}

Here we introduce the package and specify its version number:\ProvidesExplPackage{dashundergaps}{2018/11/09}
31{v2.0d}32{Dashing and underlining phantom text}
29
30

5.2.1
\gap

User interface commands

The \gap command parses for a star, optional and mandatory argument and then calls\__fmdug_gap:nnn to do the work.
33\DeclareDocumentCommand \gap { som } { \__fmdug_gap:nnn {#1}{#2}{#3} }(End definition for \gap. This function is documented on page 264.)\dashundergapssetup

Change options anywhere.
34
35\NewDocumentCommand \dashundergapssetup { m }{ \keys_set:nn {fmdug} {#1} \ignorespaces }(End definition for \dashundergapssetup. This function is documented on page 265.)\TeacherModeOn\TeacherModeOff

We provide shortcuts for turning teacher mode on or off.
\DeclareDocumentCommand \TeacherModeOn {}{ \bool_set_true:N \l__fmdug_teacher_bool }38 \DeclareDocumentCommand \TeacherModeOff {}39{ \bool_set_false:N \l__fmdug_teacher_bool }36

37(End definition for \TeacherModeOn and \TeacherModeOff. These functions are documented on page
265.)

5.2.2
\c@gapnumber

Counters

We have one user-level counter which is referenceable and holds the gap number of thecurrent gap. It can be reset to 0 to restart counting.
40\newcounter{gapnumber}(End definition for \c@gapnumber.)\c@totalgapnumber

We also keep track of all gaps ever made using another user-level counter. Since thisone is supposed to keep track of the total number of gaps, it makes little sense tomodify it at the document level. However, there may be use cases even for that andmore importantly, by making it a user-level counter it is possible to refer to the total
The dashundergaps package

270

TUGboat, Volume 39 (2018), No. 3
number of gaps easily, e.g., via \thetotalgapnumber.
41\newcounter{totalgapnumber}(End definition for \c@totalgapnumber.)\l__fmdug_extend_dim

A help register to calculate the gap width later on.
42\dim_new:N \l__fmdug_extend_dim(End definition for \l__fmdug_extend_dim.)\l__fmdug_extra_left_gap_tl\l__fmdug_extra_right_gap_tl

Two scratch token lists to enlarge the gap on the left or right side.
4344\tl_new:N \l__fmdug_extra_left_gap_tl\tl_new:N \l__fmdug_extra_right_gap_tl(End definition for \l__fmdug_extra_left_gap_tl and \l__fmdug_extra_right_gap_tl.)\l__fmdug_gap_format_tl\l__fmdug_teacher_gap_format_tl

The gap formatting is normally handled by a ulem command; which one depends on
the options used. To record the choice we store it in a token list (one for normal andone for teacher mode).
4546\tl_new:N \l__fmdug_gap_format_tl\tl_new:N \l__fmdug_teacher_gap_format_tl(End definition for \l__fmdug_gap_format_tl and \l__fmdug_teacher_gap_format_tl.)

5.2.3

Messages\msg_new:nnn {dashundergaps} {deprecated}{ The~ #1~ ‘#2’~ you~ used~ \msg_line_context: \ is~ deprecated~ and~49
there~ is~ no~ replacement.~ Since~ I~ will~ not~ guarantee~ that~50#1~ ‘#2’~ will~ be~ kept~ forever~ I~ strongly~ encourage~ you~51
to~ remove~ it~ from~ your~ document. }47

48\msg_new:nnnn {dashundergaps} {nested}{ The~ \gap command~ can’t~ be~ nested! }54{ Nesting~ doesn’t~ make~ much~ sense~ as~ the~ inner~ one~55
wouldn’t~ be~ visible.~ ~ To~ allow~ further~ processing~ it~ is~56
handled~ as~ if~ it~ hasn’t~ been~ asked~ for. }5253\msg_new:nnnn {dashundergaps} {gap-format-value}{ Unknown~ value~ for~ key~ ’#1 gap-format’! }59{ Supported~ values~ are~ ’underline’,~ ’double-underline’,\\60’dash’,~ ’dot’,~ ’wave’~ or~ ’blank’. }
57
58

5.2.4

Option handling

Here we define all the possible option keys for use either as package options or inside\dashundergapssetup. These are all straightforward assignments to variables. These
internal variables are declared by the key declarations if unknown, so they are notseparately declared beforehand.
\keys_define:nn {fmdug}{
63% ====================================
64,teacher-mode.bool_set:N = \l__fmdug_teacher_bool
65,teacher-mode.default:n = true66
,teacher-mode.initial:n = false
67% -----------------68,gap-mode .bool_set_inverse:N = \l__fmdug_teacher_bool69% ====================================
70,gap-format
71.choice:6162Frank Mittelbach

TUGboat, Volume 39 (2018), No. 3

271

In the case of dashes and even more so in the case of dots, it looks fairly ugly if they
are below the baseline as if there were text above. We therefore raise them up a bit if
the option gap-format-adjust is given (which is the default).
In the case of dots we undo exactly the amount by which they are lowered in ulem sothat they end up precisely at the baseline, in case they are followed by a real dot. In
other cases we stay a bit below the baseline.
The same is done below when the optional argument is evaluated. But we don’t dothis in teacher mode since there we will have text above and we don’t want to bumpinto that.
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93

,gap-format / underline.code:n = \tl_set:Nn \l__fmdug_gap_format_tl{ \__fmdug_gap_format_adjust:n{.4pt} \uline },gap-format / double-underline.code:n = \tl_set:Nn \l__fmdug_gap_format_tl{ \__fmdug_gap_format_adjust:n{2pt} \uuline },gap-format / dash.code:n = \tl_set:Nn \l__fmdug_gap_format_tl{ \__fmdug_gap_format_adjust:n{0pt} \dashuline },gap-format / dot.code:n = \tl_set:Nn \l__fmdug_gap_format_tl{ \__fmdug_gap_format_adjust:n{-.08ex} \dotuline },gap-format / wave
.code:n = \tl_set:Nn \l__fmdug_gap_format_tl{ \__fmdug_gap_format_adjust:n{1pt} \uwave },gap-format / blank.code:n = \tl_set:Nn \l__fmdug_gap_format_tl { \fmdug@ublank },gap-format / unknown.code:n = \msg_error:nnn{dashundergaps}{gap-format-value}{},gap-format.initial:n = underline% ====================================

This controls the raising of the gap underline by some amount. We implement it as a.choice even though it looks like a boolean.
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116

,gap-format-adjust.choice:,gap-format-adjust / true.code:n = \cs_set:Npn \__fmdug_gap_format_adjust:n ##1{ \setlength\ULdepth {##1} },gap-format-adjust / false.code:n = \cs_set_eq:NN \__fmdug_gap_format_adjust:n \use_none:n,gap-format-adjust.default:n = true
,gap-format-adjust.initial:n = true
,adjust .meta:n = { gap-format-adjust }% ====================================,teacher-gap-format.choice:,teacher-gap-format / underline.code:n = \tl_set:Nn \l__fmdug_teacher_gap_format_tl { \uline },teacher-gap-format / double-underline.code:n = \tl_set:Nn \l__fmdug_teacher_gap_format_tl { \uuline },teacher-gap-format / dash.code:n = \tl_set:Nn \l__fmdug_teacher_gap_format_tl { \dashuline },teacher-gap-format / dot.code:n = \tl_set:Nn \l__fmdug_teacher_gap_format_tl { \dotuline }The dashundergaps package

272

TUGboat, Volume 39 (2018), No. 3
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153

,teacher-gap-format / wave
.code:n = \tl_set:Nn \l__fmdug_teacher_gap_format_tl { \uwave },teacher-gap-format / blank.code:n = \tl_set:Nn \l__fmdug_teacher_gap_format_tl { \fmdug@ublank },teacher-gap-format / unknown.code:n = \msg_error:nnn{dashundergaps}{gap-format-value}{teacher-},teacher-gap-format.initial:n = blank% ====================================,gap-widen.bool_set:N = \l__fmdug_gap_widen_bool,gap-widen.default:n = true
,gap-widen.initial:n = false% -----------------,widen.meta:n = { gap-widen }% -----------------,gap-extend-minimum .dim_set:N = \l__fmdug_gap_min_dim,gap-extend-minimum .initial:n = 20pt
% -----------------,gap-extend-percent .tl_set:N = \l__fmdug_gap_percent_tl,gap-extend-percent .initial:n = 20
% ====================================,gap-numbers.bool_set:N = \l__fmdug_number_bool,gap-numbers.default:n = true
,gap-numbers.initial:n = true% -----------------,numbers.meta:n = { gap-numbers }% -----------------,gap-number-format .tl_set:N= \l__fmdug_gapnum_format_tl,gap-number-format .initial:n= \textnormal{\space (\thegapnumber)}% ====================================,display-total-gaps .bool_gset:N = \g__fmdug_display_total_gaps_bool,display-total-gaps.default:n = true
,display-total-gaps.initial:n = false% ====================================,gap-font.tl_set:N = \l__fmdug_font_tl,gap-font.default:n =,gap-font.initial:n = \bfseries

And finally the original options, now as aliases:
154
155
156
157
158
159
160
161
162
163
164

\__fmdug_gap:nnn% ====================================,teachermode.meta:n = { teacher-mode },dash.meta:n = { gap-format = dash },dot.meta:n = { gap-format = dot },displaynbgaps .meta:n = { display-total-gaps }% -----------------,phantomtext.code:n = \msg_warning:nnnn{dashundergaps}{deprecated}{option}{phantomtext}% ====================================}At last, here comes the action. \__fmdug_gap:nn expects two arguments: #1 indicateswhat kind of “underlining” is wanted (anything not recognized is ignored, in particular“–NoValue–” if \gap was used without an optional argument) and #2 is the material toproduce a gap for.
165
166

\cs_new:Npn\__fmdug_gap:nnn #1#2#3 {\group_begin:Frank Mittelbach

TUGboat, Volume 39 (2018), No. 3

273

Define the font used inside the gap. We need to do this up front since we want tomeasure the text (and that needs the correct font already).
167\l__fmdug_font_tl

Nesting is not supported so inside the gap we redefine \__fmdug_gap:nnn to raise anerror and just return the third argument if it is encountered again.
168
169
170
171
172

\cs_set:Npn \__fmdug_gap:nnn ##1##2##3{\msg_error:nn{dashundergaps}{nested}##3}

We always increment the counter for the total number of gaps, but increment thegapnumber only if we are displaying it. For the latter one we use \refstepcounter tomake it referenceable.
173
174
175

\stepcounter{totalgapnumber}\bool_xor:nnT { #1 } { \l__fmdug_number_bool }{ \refstepcounter{gapnumber} }Next we prepare for widening if that is being asked for: Measure the width of the text
and then set \l__fmdug_extend_dim to be the requested percentage divided by twoof that width (since we add it later on both sides).
176
177
178
179
180

\bool_if:NTF \l__fmdug_gap_widen_bool{\settowidth \l__fmdug_extend_dim {#3}\dim_set:Nn \l__fmdug_extend_dim{ \l__fmdug_gap_percent_tl \l__fmdug_extend_dim / 200 }Then compare it to the minimum / 2 and choose whatever is larger.
181
182\dim_compare:nNnT \l__fmdug_extend_dim < { .5\l__fmdug_gap_min_dim }{ \dim_set:Nn \l__fmdug_extend_dim { .5\l__fmdug_gap_min_dim } }Now we prepare what needs to go to the left and the right of the gap.
183
184
185
186
187

\tl_set:Nn \l__fmdug_extra_left_gap_tl{ \hbox_to_wd:nn\l__fmdug_extend_dim{} \allowbreak }\tl_set:Nn \l__fmdug_extra_right_gap_tl{ \allowbreak \hbox_to_wd:nn\l__fmdug_extend_dim{} }}And if no widening is asked for we clear these two token lists so they don’t do anything.
188{\tl_clear:N \l__fmdug_extra_left_gap_tl\tl_clear:N \l__fmdug_extra_right_gap_tl

189
190
191

}

Next comes deciding the gap format. If in teacher mode it will be whatever is in \l__fmdug_teacher_gap_tl. Otherwise, either it is based on the content of the optional
argument or, if that is not given or unknown, it will be \l__fmdug_gap_format_tl.
192
193
194

\bool_if:NTF \l__fmdug_teacher_bool{ \l__fmdug_teacher_gap_format_tl }{But before we execute any of the ulem commands we make sure that they do not
output text.
195
196
197
198

\cs_set:Npn\fmdug@box ##1 {\hbox_to_wd:nn{\box_wd:N ##1}{}}\str_case:nnF {#2}{{u} { \__fmdug_gap_format_adjust:n{.4pt}\uline }The dashundergaps package

274

TUGboat, Volume 39 (2018), No. 3{d} { \__fmdug_gap_format_adjust:n{2pt}\uuline }{w} { \__fmdug_gap_format_adjust:n{1pt}\uwave }{b} { \fmdug@ublank }{.} { \__fmdug_gap_format_adjust:n{-.08ex} \dotuline }{-} { \__fmdug_gap_format_adjust:n{0pt}\dashuline }

199
200
201
202
203

}
{ \l__fmdug_gap_format_tl }

204
205

}

206

Whatever was decided as the gap format, it needs one argument, i.e., the material(with possible gap extension on both sides).
{\l__fmdug_extra_left_gap_tl #3 \l__fmdug_extra_right_gap_tl }207

Finally we typeset the gap number if that was requested.
\bool_xor:nnT { #1 } { \l__fmdug_number_bool }{ \l__fmdug_gapnum_format_tl }

208
209

Close the group from above to keep any of the redefinitions confined.
\group_end:

210
211

}

(End definition for \__fmdug_gap:nnn.)\__fmdug_display_total_gaps:

This command will display the total number of gaps if requested. The hard-wired
formatting comes from the first version of the package.
\cs_new:Npn \__fmdug_display_total_gaps: {\vfill \centering
214\bfseries Total~ Gaps:~ \thetotalgapnumber
215 }
212
213

(End definition for \__fmdug_display_total_gaps:.)

5.2.5

Closing shop

At the end of the document we typeset the total number of gaps if requested.
\AtEndDocument{\bool_if:NT \g__fmdug_display_total_gaps_bool
218\__fmdug_display_total_gaps:
219 }
216
217

So what remains to be done is executing all options passed to the package via\usepackage.
220
221\ProcessKeysPackageOptions{fmdug}
h*packagei

References[1] Frank Mittelbach. The widows-and-orphans package. TUGboat 39:3, 252–262, 2018.
https://ctan.org/pkg/widows-and-orphans[2] LATEX3 Project Team. A collection of articles on expl3.
https://latex-project.org/publications/indexbytopic/l3-expl3/
 Frank Mittelbach
Mainz, Germany
https://www.latex-project.org
https://ctan.org/pkg/dashundergaps

Frank Mittelbach