34

I need to typeset a sentence such that just the words are underlined, and not the space between the words. Using the \underline will include the spaces between the words. I could do it with something like

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

but it is too tedious and a painful thing to do. Also, the above line produces what is intended, but the spacing of the underline from the words is uneven. It would be really helpful if this is addressed in the answer as well. The above line produces this: enter image description here

Is there a package or something easier to achieve this?

8
  • What should be done if the sentence contains only words that do not contain letters with descenders (the letters g, j, p, q, and y)? Specifically, should the lines under the words all be set tightly -- as in "The", "fox", and "over" in your screenshot -- or loosely?
    – Mico
    Sep 15, 2015 at 5:53
  • @Mico I wouldn't care as long as all the underlines are uniform. Either would work for me
    – Some guy
    Sep 15, 2015 at 6:09
  • Should punctuation marks (e.g., , and .) within and at the end of a sentence be underlined, or should they get the same treatment as whitespace, i.e., not be underlined?
    – Mico
    Sep 17, 2015 at 4:46
  • @Mico good point, no underline for punctuations.
    – Some guy
    Sep 17, 2015 at 4:54
  • Very related: tex.stackexchange.com/questions/126291/… Sep 17, 2015 at 18:32

6 Answers 6

31

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

1
  • 5
    Is there a way to modify your macros so that all non-letter characters (including punctuation characters), and not just whitespace characters, are not underlined?
    – Mico
    Sep 17, 2015 at 4:42
25

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

3
  • 1
    Wow!. This is exactly what I need. But could you explain what is happening in the newcommand* section. I see a lot of @SOULs in there, it would be really helpful I understood what those are?. Am I asking too much?
    – Some guy
    Sep 14, 2015 at 19:49
  • 1
    @Rakesh The code is quite simple, it just copied the definitions for \ul of soul.sty, renamed ul to ulw, and commented the line with \SOUL@everyspace. For details, see the documentation of package soul. It explains, how to build a driver for soul in detail. Sep 14, 2015 at 20:40
  • Is there a way to modify your macros so that all non-letter characters (including punctuation characters), and not just whitespace, are not underlined?
    – Mico
    Sep 17, 2015 at 4:42
13

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}
2
  • @Fran - Thanks for pointing out that the string.find and string.gsub functions don't handle non-ASCII utf8-encoded characters correctly. I've switched the functions to unicode.utf8.find and unicode.utf8.gsub, respectively. Now, as long as the input encoding is UTF8, the characters á and ó in "El rápido perro marrón saltó sobre el perro perezoso." are also underlined, as desired.
    – Mico
    Sep 17, 2015 at 12:09
  • I'm not ready to learn lua, but +1 anyway
    – Fran
    Sep 17, 2015 at 15:45
6

FWIW, ConTeXt provides a macro for this: \underbars. In fact, all macros for horizontal rules--\underbar, \overstrike, and understrike--have a plural version which works at the word level.

\starttext

\startlines
\underbar {The quick brown fox jumped over the lazy dog}
\underbars{The quick brown fox jumped over the lazy dog}

\overstrike {The quick brown fox jumped over the lazy dog}
\overstrikes{The quick brown fox jumped over the lazy dog}

\understrike {The quick brown fox jumped over the lazy dog}
\understrikes{The quick brown fox jumped over the lazy dog}
\stoplines

\stoptext

which gives

enter image description here

It is possible to change the location of the line. For example, if you want to lower the underline, define a new bar with a larger offset (distance from baseline).

\definebar[lowerbar][underbar][offset=-0.6]
\definebar[lowerbars][lowerbar][continue=no]

\starttext

\startlines
\lowerbar {The quick brown fox jumped over the lazy dog}
\lowerbars{The quick brown fox jumped over the lazy dog}
\stoplines
\stoptext

which gives

enter image description here

5
  • Is there an option to position the underlines in a way that they don't intersect letters with descenders?
    – Mico
    Oct 7, 2015 at 1:03
  • This is an awesome feature in ConTeXt!. Too bad such a straight forward macro isn't available in plain LaTeX.
    – Some guy
    Oct 7, 2015 at 2:26
  • Out of curiosity, what are the starttext and startlines macros?
    – Some guy
    Oct 7, 2015 at 2:27
  • 1
    @Mico: You can lower the line (i.e., change the distance between the line and the baseline of the text) so that it does not intersect with the descenders. See edited answer.
    – Aditya
    Oct 7, 2015 at 4:10
  • 1
    @Rakesh: \starttext ... \stoptext is similar to \begin{document} ... \end{document} in LaTeX. \startlines ... \stoplines treats a new line in the input as \\ ; this is meant for poems, etc., but is also useful when giving examples because then you don't have to add \\ after each line.
    – Aditya
    Oct 7, 2015 at 4:14
4

Another "simple" macro-s without packages to slit by the spaces and then use \ul of the soul package in each word:

\def\UL#1{\expandafter\nusp#1 \nil}
\def\nusp#1 #2\nil{\ul{#1} \ifx\relax#2\relax\else\nusp#2\nil\fi}

As far I see, also \UL{} work well with line breaks, with or without descenders, and some other circumstances (see MWE code). The spaces are changed to \xspaces in the MWE to avoid the ending space when the argument is followed by a punctuation symbol (i.e. \UL{Some text}.).

mwe

\documentclass[a5paper,11pt]{article}
\parskip1em\parindent0em
\usepackage{soul,xspace,xcolor}

\def\UL#1{\expandafter\nusp#1 \nil}
\def\nusp#1 #2\nil{\ul{#1}\xspace\ifx\relax#2\relax\else\nusp#2\nil\fi}

\begin{document}
% Simple test
\UL{The quick brown fox} jumped over the lazy dog.\par
% Testing line breaks
\UL{The quick brown fox jumped over the lazy dog.
The quick brown fox jumped over the lazy dog.
The quick brown fox jumped over the lazy dog.} 
% Testing underline grouped words
\UL{The {quick brown fox} jumped over \mbox{the lazy dog}.}\par
% Testing if \xspace work
\UL{The quick brown fox jumped over the lazy dog}.\par  
% Testing the rule depth with/without descenders
The quick \UL{brown fox} jumped over the \UL{lazy dog}.\par
% Testing \UL nested in other formating commands
The \textbf{\UL{quick brown fox}} jumped over the {\em \UL{lazy dog}}.\par
% Testing commands inside \UL. Needed some grouping/boxing ...  
\UL{The 
\mbox{\fboxrule.5pt\fboxsep.5pt%
\fcolorbox{orange!20!yellow}{yellow!60}{quick brown fox}} 
jumped over the lazy dog}\par
\UL{The {\textsc{quick brown fox}} jumped over the lazy dog}\par
\end{document}
2
  • This method appears to underline punctuation marks as well as words. Is there a way to modify the macro to exclude all non-letter characters, and not just whitespace characters, from the action of \UL?
    – Mico
    Sep 17, 2015 at 4:38
  • @Mico I cannot imagine a pure TeX/LaTeX way (except limit the scope of the \UL argument to real words) without many conditionals contorsions. Anyway, It's not a bug, it's a feature :)
    – Fran
    Sep 17, 2015 at 15:35
0

You can use pythonTeX too:

\documentclass{article}

\usepackage{pythontex}

\begin{document}
\begin{pythontex}
def under(string):
    spc = False
    out = ""
    for char in string:
        if spc == False:
            if char != " ":
                out= out + ("\underline{")
                out= out + char
                spc = True
        elif spc = True:
            if char == " ":
                out= out + "} "
                spc = False
            else:
                out= out + char
    out = out + "}"
    return out
\py{under(The quick brown fox jumped over the lazy dog)}
\end{pythontex}
\end{document}

I've considered that your string end is NOT a space and their is NO newline.

I can't show an output right now since I can't test the code where I am.

You must log in to answer this question.

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