Underline just words, not the space between them

Let's make soul work less:

\documentclass{article}
\usepackage{xparse,soul}

\ExplSyntaxOn
\NewDocumentCommand{\ulns}{m}
 {
  \seq_set_split:Nnn \l_tmpa_seq { ~ } { #1 }
  \seq_map_inline:Nn \l_tmpa_seq { \ul{##1}~ } \unskip
 }
\ExplSyntaxOff

\begin{document}

\ulns{The quick brown fox jumped} over the lazy dog.

\end{document}

Basically, the argument is split at spaces; every fragment is fed to \ul and a space is added. The last one is removed by \unskip.

enter image description here


Package soul provides an underlining macro \textul/\ul with the same depth. The following example copies the driver to \textulw/\ulw and leaves the spaces without underline:

\documentclass{article}

\usepackage{soul}
\makeatletter
\DeclareRobustCommand*{\textulw}{%
  \SOUL@ulwsetup
  \SOUL@
}
\newcommand*{\SOUL@ulwsetup}{%
  \SOUL@setup
  \let\SOUL@preamble\SOUL@ulpreamble
  \let\SOUL@everysyllable\SOUL@uleverysyllable
  % \let\SOUL@everyspace\SOUL@uleveryspace % \SOUL@ulsetup
  \let\SOUL@everyhyphen\SOUL@uleveryhyphen
  \let\SOUL@everyexhyphen\SOUL@uleveryexhyphen
}
\let\ulw\textulw
\makeatother

\begin{document}
\ulw{The quick brown fox jumped over the lazy dog}.
\end{document}

Result


Here's a LuaLaTeX-based solution. It doesn't require loading the soul package.

The user macro is called \ulow -- short for "underline only words". With \ulow, all non-letter glyphs, and not just whitespace, are exempt from being underlined.

The input is assumed to be UTF8-encoded. (ASCII is a proper subset of UTF8; thus, if your input file is pure ASCII, it's UTF8 automatically.)

If the argument of \ulow contains letters with descenders (g, j, p, q, or y), the lines below all words will be set with a generous amount of vertical whitespace. Conversely, if no letters with descenders are present, the spacing will be set more tightly. If you want the more generous spacing in all cases, simply replace the five-line if construct in the lua function with s = unicode.utf8.gsub (s, "(%a+)" , "\\underline{%1\\vphantom{p}}" ). (Even if you don't know Lua syntax, you may be able to guess that the term %a+ "captures" entire words, where a "word" can be any contiguous groups of uppercase and lowercase letters.)

enter image description here

% !TEX TS-program = lualatex
\documentclass{article}

\usepackage{fontspec}
\setmainfont{Latin Modern Roman} % set main document font

%% Lua-side code
\usepackage{luacode}
\begin{luacode}
function ulow ( s )
   if unicode.utf8.find ( s, "[gjpqy]") then -- any letters with descenders?
      s = unicode.utf8.gsub (s, "(%a+)" , "\\underline{%1\\vphantom{p}}" ) 
   else
      s = unicode.utf8.gsub (s, "(%a+)" , "\\underline{%1}" )
   end
   return ( tex.sprint ( s ) )
end
\end{luacode}

%% TeX-side code
\newcommand\ulow[1]{\directlua{ ulow ( \luastring {#1} ) }}

\begin{document}
\frenchspacing
\ulow{The quick brown fox jumps over the lazy dog.}

\bigskip
\ulow{El rápido zorro marrón saltó sobre el perro perezoso.}

\bigskip
\ulow{And, but; let: show? that. where (now), èéà äöü ÄÖÜß!}

\end{document}