135

LaTeX has soul. ConTeXt, of course, has beautifully marked up "sharpie" highlighting, where you can put sweet hightligher under your text for free!

From randomdeterminism.wordpress.com

In the pictured example, you can see highlights that not only highlight, but that do it with cool edges that look so good, you can smell fumes! The fearless author says he'll show how that was done in another post; this one shows only how to lay down a straight, yellow box. But, it is possible to do word-wrapped highlighting with cool-shapped boxes within ConTeXt, presumably.

The trouble for me is that getting into the ConTeXt world is steep climb right now, given the state of documentation and the flux of changes, so I need to continue with LaTeX. But the real problem is that now I can't live without the Organic-Looking Highlights of Awesomeness in that picture!

As I said, Non-Organic-Looking Highlights of the Ordinary are easy with the soul package:

\hl{Some awesome text that word wraps fine.}

It just taunts me on the page with its straight lines. So, if I sacrifice word wrapping, I can use PGF/TiKz and create the following macro:

\newcommand\hl[1]{%
    \tikz[baseline,%
      decoration={random steps,amplitude=1pt,segment length=15pt},%
      outer sep=-15pt, inner sep = 0pt%
    ]%
   \node[decorate,rectangle,fill=navcolor,anchor=text]{#1\xspace};%
}%

Which creates something like this:

A picture of how my TiKz highlighter looks

Now, my color is too dark and my randomness needs adjusting, but you see that a similar effect could be achieved.

My trouble is that I don't understand TeX enough to dig into the Soul package and add the TiKz/PGF goodness that it so sorely needs. So, I leave it to you guys. Anyone want to tackle this?

Or is it yet another piece of fruit, just a touch out of reach, that shall have to wait until ConTeXt settles down?

  • 1
    What is unstable about ConTeXt? Are you sure that you're using "stable" and not "beta"? – Mica Nov 26 '10 at 01:41
  • 1
    Installing it on a Mac, for one. Also, there is nothing that makes good use of the project system that it implements. PGF/TiKz support worked after I dl'd a nightly from that site. All and all, it was probably fine and perhaps "unstable" was unintentionally unfair. It would have been better to say, "There isn't enough consistent and organized documentation around ConTeXt to efficiently get up and running on with it, yet." I look forward to going that direction in the future, however. – Andrew Starks Nov 26 '10 at 01:55
  • 1
    If you look at http://wiki.contextgarden.net/Main_Page -- about half way down the page, under the heading "documentation," there is a ton of documentation. As I am learning context now (coming from latex), the first page of the wiki, the mailing list, and this site have answered all the questions I've had so far. – Mica Nov 26 '10 at 04:46
  • 12
    As far as I can tell, the slight "wigglyness" in the picture is just an optical illusion. The lines seem to be perfectly straight. – Caramdir Nov 26 '10 at 05:18
  • @Caramdir: By lucifer's beard! I believe that you are right! Hmmm... You do agree that they look ever so slightly organic, no? – Andrew Starks Nov 26 '10 at 22:43
  • 2
    @Mica: I've read my share of ConTeXt documentation (see: simplefonts). I think, as alpha software, if you have the time, patience and brains, ConTeXt can work really well. For me, getting past the scattershot approach to organization, which if it is there at all is a mixture of fantastic, incomplete, out of date or wrong, wasn't worth it yet. I'm uber impressed with the potential, but for me, the capacity of greatness for any tech is boxed in by its documentation. I would have chosen to have the documentation be written first and code to that, but they chose to let it slide. That's a shame. – Andrew Starks Nov 26 '10 at 23:05
  • is it possible to (re)use the code from @gusbrs within a package in the CTAN, with a mention of author and link, obviously , with a LPPL license or an other license ? Tks – cpxo Sep 01 '23 at 14:10
  • @cpxo The answer by @ gusbrs was originally published under CC BY-SA 3.0 see https://tex.stackexchange.com/posts/429389/timeline . This means that if you want to reuse the code, you have to give attribution and share it under a similar license. I'm not a lawyer, but LPPL probably does not count as similar enough. You would have to use some other CC BY-SA license. Besides these legal restrictions, it would be nice if you would ask the author if they are OK with the code becoming a package. – samcarter_is_at_topanswers.xyz Sep 01 '23 at 15:04
  • Many users, myself included, often post answers which just get the job done in the specific case presented in the question. For packages, many people have higher standards and thus some might not be comfortable to see their answers converted into a package. Please ask first. – samcarter_is_at_topanswers.xyz Sep 01 '23 at 15:06

7 Answers7

143

Much to my surprise, this is doable, by combining TikZ and soul. I don't think I like how it looks (though to each their own), but it was a fun challenge. The idea is to use TikZ to draw the necessary boxes up to line breaks, and then restart each box on a new line. But how to do this? Well, it turns out that soul works by inserting things after every possible hyphenation point (which it calls "syllables", a terminology misuse I find irritating)—importantly, this includes the beginnings and ends of words. And the only time we ever see a line break is after a hyphenation point! This is good: we'll use soul to insert a TikZ node at each syllable break, and then draw them together. For this, we use TikZ's remember picture and overlay options; the former enables you to refer to nodes in the labeled picture from outside, and the latter makes the picture take up no space.

To implement this, we surround each hyphenation unit with two "marks" (TikZ pictures). The "start" mark checks to see if it's on a new line; if it is, it draws the highlighting rectangle from the last recorded start position to the last recorded end position, and then records the new start position. The "stop" mark just records the new stop position. We also have to make sure that at a hyphenation point, the stop position is after the hyphen. And of course, we surround the whole picture with a start position and a stop position. Note that due to the remember picture machinery, you have to compile the document twice.

Here's a working document with ragged highlights:

\documentclass{minimal}
\usepackage{soul}
\usepackage{tikz}
\usetikzlibrary{calc}
\usetikzlibrary{decorations.pathmorphing}

\makeatletter

\newcommand{\defhighlighter}[3][]{%
  \tikzset{every highlighter/.style={color=#2, fill opacity=#3, #1}}%
}

\defhighlighter{yellow}{.5}

\newcommand{\highlight@DoHighlight}{
  \fill [ decoration = {random steps, amplitude=1pt, segment length=15pt}
        , outer sep = -15pt, inner sep = 0pt, decorate
        , every highlighter, this highlighter ]
        ($(begin highlight)+(0,8pt)$) rectangle ($(end highlight)+(0,-3pt)$) ;
}

\newcommand{\highlight@BeginHighlight}{
  \coordinate (begin highlight) at (0,0) ;
}

\newcommand{\highlight@EndHighlight}{
  \coordinate (end highlight) at (0,0) ;
}

\newdimen\highlight@previous
\newdimen\highlight@current

\DeclareRobustCommand*\highlight[1][]{%
  \tikzset{this highlighter/.style={#1}}%
  \SOUL@setup
  %
  \def\SOUL@preamble{%
    \begin{tikzpicture}[overlay, remember picture]
      \highlight@BeginHighlight
      \highlight@EndHighlight
    \end{tikzpicture}%
  }%
  %
  \def\SOUL@postamble{%
    \begin{tikzpicture}[overlay, remember picture]
      \highlight@EndHighlight
      \highlight@DoHighlight
    \end{tikzpicture}%
  }%
  %
  \def\SOUL@everyhyphen{%
    \discretionary{%
      \SOUL@setkern\SOUL@hyphkern
      \SOUL@sethyphenchar
      \tikz[overlay, remember picture] \highlight@EndHighlight ;%
    }{%
    }{%
      \SOUL@setkern\SOUL@charkern
    }%
  }%
  %
  \def\SOUL@everyexhyphen##1{%
    \SOUL@setkern\SOUL@hyphkern
    \hbox{##1}%
    \discretionary{%
      \tikz[overlay, remember picture] \highlight@EndHighlight ;%
    }{%
    }{%
      \SOUL@setkern\SOUL@charkern
    }%
  }%
  %
  \def\SOUL@everysyllable{%
    \begin{tikzpicture}[overlay, remember picture]
      \path let \p0 = (begin highlight), \p1 = (0,0) in \pgfextra
        \global\highlight@previous=\y0
        \global\highlight@current =\y1
      \endpgfextra (0,0) ;
      \ifdim\highlight@current < \highlight@previous
        \highlight@DoHighlight
        \highlight@BeginHighlight
      \fi
    \end{tikzpicture}%
    \the\SOUL@syllable
    \tikz[overlay, remember picture] \highlight@EndHighlight ;%
  }%
  \SOUL@
}
\makeatother

\begin{document}
  Lorem ipsum \highlight{dolor sit amet, consectetur adipis-icing elit, sed do
eiusmod tempor} incididunt ut labore et dolore magna aliqua. Ut enim ad minim
veniam, quis nostrud exercitation \highlight[red]{ullamco $laboris$ nisi ut
aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit} in
voluptate velit esse cillum dolore eu fugiat nulla pariatur.  Excepteur sint
occaecat \highlight[green, draw=blue]{cupidatat non proident,
suntinculpaquiofficiadeseruntmollitanimidestlaborum.
Loremipsumdolorsitametconsecteturadipisicingelitseddoeiusmodtemporincididuntutlabore-etdoloremagnaaliqua.}
I suppose I could write some more text here.
\end{document}

This produces the following output:

Lipsum text with ragged highlights.

There are two formatting modes: \defhighlighter[misc]{color}{opacity}, which sets the fill color to color, the fill opacity to opacity, and styles every highlighter block (via every highlighter) with those plus the misc options. Secondly, for a specific block of highlighted text, you can use \highlight[tikz-opts]{...} to set options locally. These are both demonstrated in the above code.

There are currently four caveats. The first is that this will write one line to your aux file for each syllable break in the highlighted text, in addition to one for the beginning of each, one for the end of each, and one for each line break after a (real or inserted) hyphen. For instance, the example document wrote 222 lines to my aux file. This may or may not be a problem for you, but I'm not sure how to make it better. The second is that strange things happen if you let highlighting extend over a page break. (Although I might be able to fix this one….) The third is that I don't see a way to draw the highlighting in the background. (Because it's drawn so late, the pgfonlayer environment doesn't work.) This is why the text looks faded. You can adjust the opacity value if you don't like this, but I don't see how to avoid it (suggestions?). And the fourth is that I guessed the height of the font, because while I figure this should be a dimension in TeX somewhere, I don't know where (again, suggestions?).

  • 3
    @Antal, very nice! FYI, font dimensions can be found in (surprise) \fontdimenN, where N is a number, e.g., \fontdimen5 gives the ex-height. For your case, you might consider something like \setbox0=\hbox{some text here}\@tempdima=\ht0. – Geoffrey Jones Nov 27 '10 at 02:38
  • @Geoffrey: Thanks, I'll check out \fontdimen! I thought about the boxing/unboxing, but the problem is that it it highlights an area of the screen, rather than scannable text (and, of course, we can't know the tallest/deepest character in the font ahead of time). Although it might be possible to leverage soul's process to keep track of the height…. – Antal Spector-Zabusky Nov 27 '10 at 03:24
  • 17
    @Antal... I'm freakin' speechless... This is quite possibly the coolest piece of TeXporn I've ever seen. Thank you! It's funny that how in the grand scheme of things, this probably doesn't matter to anyone, but now that you've done it, it's essential to my technical documentation. :) Also, I'm going to see if I can't apply the fadings library to this and some random overlapping to create the illusion of the marker running dry, the author flipping the tip and then restarting---just slightly overlapping his/her last starting point. After that, I'll start actually writing my documentation. :) – Andrew Starks Nov 27 '10 at 04:04
  • @Antal: A thought for you. In the document, could you start every paragraph with a tikz node that you could use to either: A) set the depth of the paragraph to be over your highlighting, B) be a place where you dump marginpar code so that you can mark the paragraph as significant in a specified way (determined by the highlighter(s) that you used) in the margin and flush with the top of the paragraph or C) do nothing because no highlighter was used. This would create some sort of link/reference to the paragraph... \thispar I might be way out there, as I know little about TeX internals. – Andrew Starks Nov 27 '10 at 04:15
  • @Antal: This code (copied from the interweb) might be useful for jamming a node into the start of every paragraph: \let\mypkg@@everypar\everypar \newtoks\mypkg@everypar \mypkg@everypar\expandafter{\the\everypar} \mypkg@@everypar{\mypkgs@ownstuff\the\mypkg@everypar} \def\mypkgs@ownstuff{% % } \let\everypar\mypkg@everypar – Andrew Starks Nov 27 '10 at 06:04
  • 1
    @Andrew: \everypar is the way to go, yes (I think there's a package which provides hooks), though I don't know much about it. – Antal Spector-Zabusky Nov 28 '10 at 15:05
  • Does anyone know of a way in lualatex to get it to automatically re-build? Mine builds it once with the boxes all wrong, then if I manually rebuild, it looks correct. I'll research myself, I just thought if someone knew, I'd ask here. – Andrew Starks Nov 30 '10 at 16:20
  • 1
    @Andrew: You'll have to build it twice (as I mentioned in my answer)—TeX can't know where the text is until after the first run. And I think there was a question about automatically rebuilding in luatex; if I remember correctly the answer was "no, use a makefile or other external tool." – Antal Spector-Zabusky Nov 30 '10 at 17:41
  • @AntalS-Z: Great! did you get ideas regarding the caveats in the meantime, such as the faded text? This answer could lead to a very nice post on our blog. – Stefan Kottwitz Oct 27 '11 at 13:28
  • @Stefan: Thank you! I have some thoughts (indeed, there are some helpful tips on this very site), but I'd have to sink my teeth back into the code to test, extend, etc., which I don't expect to have time to do soon (fellowship/graduate school applications, midterms, etc.). I'll try to update this answer when I can, though. – Antal Spector-Zabusky Oct 30 '11 at 09:42
  • 7
    This is IMO well worth turning into a package! – Mohan Jan 02 '13 at 18:31
  • 1
    Has there been a solution found for the faded text issue? I've found nothing newer than this answer… – pascal May 24 '13 at 02:08
  • Just wanted to note that this solution works also for \usepackage[cmyk]{xcolor}, which may break the usual {soul} \hl command... – sdaau Nov 19 '14 at 22:38
  • 1
    @AntalSpector-Zabusky There are some unanswered questions (e.g., http://tex.stackexchange.com/questions/251252/box-highlighting-of-text-with-rounded-corners-and-hyphenation-support?noredirect=1&lq=1) about how to make your great solution having rounded corners. I hope it is possible but most people including me have not sufficient LaTeX knowledge to adapt your solution. Could you extend your solution to rounded corners? – Niklas Peter Nov 27 '16 at 09:48
  • @NiklasPeter, perhaps you will be interested in an attempt I made on the issue: http://tex.stackexchange.com/a/363828/105447. – gusbrs Apr 09 '17 at 02:42
43

I can't see a difference between the picture you provided and

\documentclass{article}
\usepackage[width=5.05cm]{geometry}
\usepackage{color,soul}

\begin{document}
\noindent\hl{Highlighting} text feels good.
You can draw attention of people to a \hl{word} or perhaps 
\hl{even a whole sentence that spans across multiple lines
in such a way that hyphenation etc. are not affected.}
\end{document}

rendered example

I suspect getting TeX to draw actual randomly wiggly highlights that work well with line breaks is a lot more work.

Caramdir
  • 89,023
  • 26
  • 255
  • 291
  • 9
    You beat me to it while I got distracted with weird names for SVG colours. My example was going to use (via xcolor) "PaleGoldenrod" or perhaps "PapayaWhip". – Will Robertson Nov 26 '10 at 05:47
  • 3
    People must have had fun inventing all those color names. – Caramdir Nov 26 '10 at 05:52
  • 1
    Snap! You are correct. Maybe I should just grow up and go this route? It's fun to puzzle this out, but I'm just not a TeXpert. I think you really have to have a different sort of brain to go that far. – Andrew Starks Nov 26 '10 at 22:46
  • 3
    +1 This is perfect for my purpose. I’ve also defined a new command to set the color per highlight instead of globally: \newcommand{\hlc}[2][yellow]{ {\sethlcolor{#1} \hl{#2}} }. And its usage: \hlc[CornflowerBlue]{Highlight}. – Gumbo Mar 16 '11 at 10:58
  • Caramdir and @WillRobertson - note that by using \usepackage[cmyk]{xcolor} before \usepackage{soul} in the above code (instead of just {color,soul}), the highlight gets all corrupted, see screenshot.png; any ideas about how to handle that case? Seems to be so, only if [cmyk] if specified for {xcolor}... – sdaau Nov 19 '14 at 22:23
  • 1
    @sdaau: No idea. You should ask a separate question. – Caramdir Nov 20 '14 at 02:44
  • @Caramdir Your solution would be great for me except that 1. \hl does not accept math and 2. I need to be able to change the color on the fly as with \colorbox. Any suggestion? ("Combining TikZ and soul" is not for the faint of heart.) – schremmer May 19 '16 at 13:19
  • @Caramdir I just saw Gumbo's new command which takes care of 2. But there remains the matter of \hl not accepting math. – schremmer May 19 '16 at 14:26
  • @Caramdir How can I redefine soul's \texthl so as to be able to set the color in situ rather than in the preamble, like say \colorbox? – schremmer Jul 11 '16 at 21:52
26

If figured that I can adjust my code written for Test if a paragraph has a page break in it? to do underlining and also highlighting. This solution marks the begin and the end of the text with both TikZ and zref marker (the latter to test for page breaks) and draws the lines using tikz between the markers. The current line width is taken into account and new markers are automatically set at every paragraph break. This has the benefit that the normal text typesetting is not influenced at all! I'm working to generalize this (see Place TikZ coordinate or \zlabel at every (base)line and Installing background and foreground page layers with TikZ) and provide a better UI in form of a package as soon I find time.

\documentclass[twoside,11pt]{book}

\usepackage{zref-abspage}
\usepackage{zref-user}
\usepackage{tikz}
\usepackage{atbegshi}
\usetikzlibrary{calc,decorations.pathmorphing}

\makeatletter
\newcommand{\currentsidemargin}{%
  \ifodd\zref@extract{textarea-\thetextarea}{abspage}%
    \oddsidemargin%
  \else%
    \evensidemargin%
  \fi%
}

\newcounter{textarea}
\newcommand{\settextarea}{%
   \stepcounter{textarea}%
   \zlabel{textarea-\thetextarea}%
   \begin{tikzpicture}[overlay,remember picture]
    % Helper nodes
    \path (current page.north west) ++(\hoffset, -\voffset)
        node[anchor=north west, shape=rectangle, inner sep=0, minimum width=\paperwidth, minimum height=\paperheight]
        (pagearea) {};
    \path (pagearea.north west) ++(1in+\currentsidemargin,-1in-\topmargin-\headheight-\headsep)
        node[anchor=north west, shape=rectangle, inner sep=0, minimum width=\textwidth, minimum height=\textheight]
        (textarea) {};
  \end{tikzpicture}%
}


\usepackage{lipsum}
\newcommand\xlipsum[1][]{{\let\par\relax\lipsum*[#1]}}

\tikzset{tikzul/.style={yshift=-.75\dp\strutbox}}

\newcounter{tikzul}%
\newcommand\tikzul[1][]{%
    \begingroup
    \global\tikzullinewidth\linewidth
    \def\tikzulsetting{[#1]}%
    \stepcounter{tikzul}%
    \settextarea
    \zlabel{tikzul-begin-\thetikzul}%
    \tikz[overlay,remember picture,tikzul] \coordinate (tikzul-\thetikzul) at (0,0);% Modified \tikzmark macro
    \ifnum\zref@extract{tikzul-begin-\thetikzul}{abspage}=\zref@extract{tikzul-end-\thetikzul}{abspage}
    \else
        \AtBeginShipoutNext{\tikzul@endpage{#1}}%
    \fi
    \bgroup
    \def\par{\ifhmode\unskip\fi\egroup\par\@ifnextchar\noindent{\noindent\tikzul[#1]}{\tikzul[#1]\bgroup}}%
    \aftergroup\endtikzul
    \let\@let@token=%
}

\newlength\tikzullinewidth

\def\tikzul@endpage#1{%
\setbox\AtBeginShipoutBox\hbox{%
\box\AtBeginShipoutBox
\hbox{%
\begin{tikzpicture}[overlay,remember picture,tikzul]
\draw[#1]
    let \p1 = (tikzul-\thetikzul), \p2 = ([xshift=\tikzullinewidth+\@totalleftmargin]textarea.south west) in
    \ifdim\dimexpr\y1-\y2<.5\baselineskip
        (\x1,\y1) -- (\x2,\y1)
    \else
        let \p3 = ([xshift=\@totalleftmargin]textarea.west) in
        (\x1,\y1) -- +(\tikzullinewidth-\x1+\x3,0)
        % (\x3,\y2) -- (\x2,\y2)
        (\x3,\y1)
       \myloop{\y1-\y2+.5\baselineskip}{%
           ++(0,-\baselineskip) -- +(\tikzullinewidth,0)
       }%
    \fi
;
\end{tikzpicture}%
}}%
}%

\def\endtikzul{%
    \zlabel{tikzul-end-\thetikzul}%
    \ifnum\zref@extract{tikzul-begin-\thetikzul}{abspage}=\zref@extract{tikzul-end-\thetikzul}{abspage}
    \begin{tikzpicture}[overlay,remember picture,tikzul]
        \expandafter\draw\tikzulsetting
            let \p1 = (tikzul-\thetikzul), \p2 = (0,0) in
            \ifdim\y1=\y2
                (\x1,\y1) -- (\x2,\y2)
            \else
                let \p3 = ([xshift=\@totalleftmargin]textarea.west), \p4 = ([xshift=-\rightmargin]textarea.east) in
                (\x1,\y1) -- +(\tikzullinewidth-\x1+\x3,0)
                (\x3,\y2) -- (\x2,\y2)
                (\x3,\y1)
                \myloop{\y1-\y2}{%
                    ++(0,-\baselineskip) -- +(\tikzullinewidth,0)
                }%
            \fi
        ;
    \end{tikzpicture}%
    \else
    \settextarea
    \begin{tikzpicture}[overlay,remember picture,tikzul]
        \expandafter\draw\tikzulsetting
            let \p1 = ([xshift=\@totalleftmargin,yshift=-.5\baselineskip]textarea.north west), \p2 = (0,0) in
            \ifdim\dimexpr\y1-\y2<.5\baselineskip
                (\x1,\y2) -- (\x2,\y2)
            \else
                let \p3 = ([xshift=\@totalleftmargin]textarea.west), \p4 = ([xshift=-\rightmargin]textarea.east) in
                (\x3,\y2) -- (\x2,\y2)
                (\x3,\y2)
                \myloop{\y1-\y2}{%
                    ++(0,+\baselineskip) -- +(\tikzullinewidth,0)
                }
            \fi
        ;
    \end{tikzpicture}%
    \fi
    \endgroup
}

\def\myloop#1#2#3{%
    #3%
    \ifdim\dimexpr#1>1.1\baselineskip
        #2%
        \expandafter\myloop\expandafter{\the\dimexpr#1-\baselineskip\relax}{#2}%
    \fi
}

\makeatother

\begin{document}

text text text text text text text text text text text
text text text text text text text text text text text
text text text text text text text text text text text
text text \tikzul[red]{text text text text text text text text text
text text text text text text text text text text text
text text text text} text text text text text text text
text text text text text text text text text text text
text text text text text text text text text text text

{\tikzset{tikzul/.style={yshift=1ex}}

Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Ut purus elit,
vestibulum ut, placerat ac, adipiscing vitae, felis. Curabitur dictum gravida
mauris. Nam arcu libero, nonummy eget, consectetuer id, vulputate a, magna.
Donec vehicula augue eu neque. Pellentesque habitant morbi tristique senectus
et netus et malesuada fames ac turpis egestas. Mauris ut leo. Cras viverra
metus rhoncus sem. Nulla et lectus vestibulum urna fringilla ultrices. Phasellus
eu tellus sit amet tortor gravida placerat. Integer sapien est, iaculis in, pretium
quis, viverra ac, nunc. Praesent eget sem vel leo ultrices bibendum. \tikzul[line width=1.5\ht\strutbox,semitransparent,yellow]{Aenean
faucibus. Morbi dolor nulla, malesuada eu, pulvinar at, mollis ac, nulla. Curabitur auctor semper nulla. Donec varius orci eget risus. Duis nibh mi, congue
eu, accumsan eleifend, sagittis quis, diam. Duis eget orci sit amet orci dignissim
rutrum.}
text text text text text text text text text text text
text text text text text text text text text text text
text text text text text text text text text text text
test
}

\begin{quote}
text text text text text text text text text text text
text text text text text text text text text text text
text text text text text text text text text text text
text text \tikzul[red]{text text text text text text text text text
text text text text text text text text text text text
text text text text text text text text text text text
text text text text text text text text text text text
text text text text text text text text text text text
text text text text} text text text text text text text
text text text text text text text text text text text
text text text text text text text text text text text
\end{quote}

{\tikzset{tikzul/.style={yshift=.5ex}}

\begin{quote}
text text text text text text text text text text text
text text text text text text text text text text text
\begin{quote}
text text text text text text text text text text text
text text \tikzul[green]{text text text text text text text text text
text text text text text text text text text text text
text text text text 
text text text text text text text
text text text text text text text text text text text
text text text text text text text text text text text
text text text text} text text text text text text text
\end{quote}
text text text text text text text text text text text
text text text text text text text text text text text
\end{quote}

\large
new text text text text text text text text text text text
text text \tikzul[red]{first text text text text text text text text
teXt teXt teXt teXt teXt teXt teXt teXt teXt teXt teXt
teXt teXt teXt teXt teXt teXt teXt teXt teXt teXt teXt
teXt teXt teXt last} teXt teXt teXt teXt teXt teXt teXt
text text text text text text text text text text text
text text text text text text text text text text text

text text text text text text text text text text text
text text text text text text text text text text text
text text \tikzul[red]{text text text text text text text text text
text text\par\noindent text text text text text text text text text
text text text text} text text text text text text text
text text text text text text text text text text text
text text text text text text text text text text text
}

\begin{itemize}
    \item \tikzul{test test test} test
    \item test \tikzul{test test test} test
    \item aa \tikzul{test test test} test
    \item b \tikzul{test test 
text text text text text text text text text text text
text text text text text text text text text text text
text text text text text text text text text text text
        test} test
\end{itemize}

text text text text text text text text text text text
text text text text text text text text text text text
text text text text text text text text text text text
text text text text text text text text text text text
text text \tikzul[red]{text text text text text text text text text
text text

 text text text text text text text text text
text text text text} text text text text text text text
text text text text text text text text text text text
text text text text text text text text text text text

text text text text text text text text text text text
text text text text text text text text text text text
text text \tikzul[red]{text text text text text text text text text
text text

\noindent text text text text text text text text text
\par text text text text} text text text text text text text
text text text text text text text text text text text
text text text text text text text text text text text


\end{document}

enter image description here

Martin Scharrer
  • 262,582
  • 2
    Is it possible to put the shading area in the background, while the text would be in foreground? – cacamailg Dec 11 '15 at 14:22
  • Dear Martin. A big thank you for this, but I have some issues with it. Can you check: http://tex.stackexchange.com/q/334528/74646 ? – robert Oct 17 '16 at 19:18
  • @Martin It is not working with tabularx: \begin{tabular}{p{4em}p{4em}} test & \tikzul{some text here test test test test }\\ \end{tabular}. The text and underline is misaligned. – jiewuza Dec 03 '18 at 03:23
15

This is an old question, but recent question linked to it, so well... The image in the question is from a blog post of mine. Due to some visual artifacts, Andrew thought that the highlighting looked "organic" but those were just straight line.

To set the record straight, here is a ConTeXt solution with organic highlights. The main idea is to use Metapost to draw the highlight; I randomize the line to give the effect that it has been drawn by hand:

\startuseMPgraphic{highlight:random}
    fill
      ((0,RuleDepth) -- (RuleWidth, RuleDepth) --
       (RuleWidth, RuleFactor*RuleThickness) -- (0, RuleFactor*RuleThickness) -- cycle)
       randomized 4bp
        shifted (0,RuleFactor*RuleOffset)
        withcolor RuleColor 
        ;
    setbounds currentpicture to unitsquare xysized(RuleWidth,RuleHeight) ;
\stopuseMPgraphic

\definebar[highlight]
          [
            mp=highlight:random,
            order=background,
            rulethickness=1.1,
            unit=ex,
            offset=-0.25,
            continue=yes,
            color=yellow,
          ]

which gives:

enter image description here

Just to make sure that there are no visual artifacts giving the impression that the highlights are ragged, here is the image when I draw the outline of the highlight (instead of filling it in)

enter image description here

These are too random for my taste. Slightly more subtle is:

enter image description here

or with fill rather than draw:

enter image description here

which is achieved using:

\startuseMPgraphic{highlight:random}
    fill
      ((0 randomized -5pt,RuleDepth) -- (RuleWidth randomized 5pt, RuleDepth) --
       (RuleWidth randomized 5pt, RuleFactor*RuleThickness) -- (0 randomized -5pt, RuleFactor*RuleThickness) -- cycle)
       randomized 2bp
        shifted (0,RuleFactor*RuleOffset)
        withcolor RuleColor 
        ;
    setbounds currentpicture to unitsquare xysized(RuleWidth,RuleHeight) ;
\stopuseMPgraphic

\definebar[highlight]
          [
            mp=highlight:random,
            order=background,
            rulethickness=1.1,
            unit=ex,
            offset=-0.25,
            continue=yes,
            color=yellow,
          ]
Aditya
  • 62,301
  • Could you write a whole code? please. – Mamoun Mohammed Jan 13 '21 at 13:53
  • 1
    @MomounMohammed: Both examples are the whole code! You use it using \highlight{text to be highlighted}. – Aditya Jan 13 '21 at 20:56
  • \documentclass{book} \startuseMPgraphic{highlight:random} fill((0 randomized -5pt,RuleDepth) -- (RuleWidth randomized 5pt, RuleDepth) -- (RuleWidth randomized 5pt, RuleFactor*RuleThickness) -- (0 randomized -5pt, RuleFactor*RuleThickness) -- cycle)randomized 2bpshifted (0,RuleFactor*RuleOffset)withcolor RuleColor ;setbounds currentpicture to unitsquare xysized(RuleWidth,RuleHeight) ; \stopuseMPgraphic \definebar[highlight][mp=highlight:random,order=background,rulethickness=1.1,unit=ex,offset=-0.25,continue=yes,color=yellow,] \begin{document} \highlight{text to} \end{document} – Mamoun Mohammed Jan 14 '21 at 08:51
  • didn't work with me – Mamoun Mohammed Jan 14 '21 at 08:51
  • 3
    This is a [ConTeXt] (http://contextgarden.net/) solution and it will not work with LaTeX. – Aditya Jan 16 '21 at 03:45
12

Well, this is an old question, but some issues remain unsolved, so I'm trying my hand here. This is a variation of Antal Spector-Zabusky's answer in which I try to handle some of its problems, namely page breaks, and getting the highlighting in the background.

The starting point, as mentioned, is soul. But we use it here exclusively to set tikmarks at the start and end of each hyphenation point. This is done only to get coordinates, no drawing is done at this point.

Drawing is done by tikz AtBeginShipout, based on the full set of tikzmarks made before. It is actually done AtBeginShipoutUpperLeft, which means it is drawn "below" the text.

But, in order to be able to draw the right thing at the right place in the right page, we have to index the tikzmarks. Thus they got names in the form p<page number>.d<decoration number>.<hyphenation point number>. Based on this indexing, we can scan the tikzmarks sequentially, decoration by decoration, page by page. Each line of each "decoration" is handled individually, and at the end of the day, we get (x,y) coordinates for each beginning and end of line, at baseline. First line, last line, middle lines and single lines are handled separately at the moment of drawing, thus allowing for different shapes for each of these cases (this is not used here, but may be quite useful).

The actual use is done by the macro \textdecor[...]{Text} which can take an optional argument which is then appended to the TikZ style at the moment of drawing.

The result:

enter image description here

(page break)

enter image description here

Closer:

enter image description here

The code:

\documentclass[12pt,DIV=8]{scrartcl}
\usepackage{soulutf8}
\usepackage{atbegshi}
\usepackage{etoolbox}
\usepackage{tikz}
\usetikzlibrary{tikzmark,calc,decorations.pathmorphing}


\colorlet{tdcolor}{yellow!35}

\makeatletter

\newlength{\txtdec@depth}
\setlength{\txtdec@depth}{.5ex}
\newlength{\txtdec@height}
\setlength{\txtdec@height}{\f@size pt} % sensible default

\newcounter{txtdec@hyphmark}
\newcounter{txtdec@decormark}
\setcounter{txtdec@decormark}{1}
\newcounter{txtdec@hyphdraw}
\setcounter{txtdec@hyphdraw}{1}
\newcounter{txtdec@decordraw}
\setcounter{txtdec@decordraw}{1}

\newcounter{txtdec@thenextpage}

\newtoggle{txtdec@unfinisheddecor}
\newtoggle{txtdec@stayonpage}
\newtoggle{txtdec@stayondecor}
\newtoggle{txtdec@stayonline}

% from https://tex.stackexchange.com/a/33765/105447
\newcommand{\gettikzxy}[3]{%
  \tikz@scan@one@point\pgfutil@firstofone#1\relax
  \edef#2{\the\pgf@x}%
  \edef#3{\the\pgf@y}%
}

% the drawing macros

\tikzset{%
  defaultdecor/.style={%
    fill=tdcolor,
    decoration = {random steps, amplitude=1pt, segment length=10pt},
    outer sep = -15pt,
    inner sep = 0pt,
    decorate}%
}

\newcommand{\txtdec@draw@all}{%
  \tikzset{thisdecor/.style/.expanded=\csuse{decor@tikz@style@\thetxtdec@decordraw}}%
  \path[defaultdecor, thisdecor]
        ($(\Xbegin,\Ybegin)+(0,-\txtdec@depth)$) rectangle
        ($(\Xend,\Yend)+(0,\txtdec@height-\txtdec@depth)$) ;
}

\newcommand{\txtdec@draw@begin}{%
  \tikzset{thisdecor/.style/.expanded=\csuse{decor@tikz@style@\thetxtdec@decordraw}}%
  \path[defaultdecor, thisdecor]
        ($(\Xbegin,\Ybegin)+(0,-\txtdec@depth)$) rectangle
        ($(\Xlineend,\Ylineend)+(0,\txtdec@height-\txtdec@depth)$) ;
}

\newcommand{\txtdec@draw@middle}{%
  \tikzset{thisdecor/.style/.expanded=\csuse{decor@tikz@style@\thetxtdec@decordraw}}%
  \path[defaultdecor, thisdecor]
        ($(\Xlinebegin,\Ylinebegin)+(0,-\txtdec@depth)$) rectangle
        ($(\Xlineend,\Ylineend)+(0,\txtdec@height-\txtdec@depth)$) ;
}

\newcommand{\txtdec@draw@end}{%
  \tikzset{thisdecor/.style/.expanded=\csuse{decor@tikz@style@\thetxtdec@decordraw}}%
  \path[defaultdecor, thisdecor]
        ($(\Xlinebegin,\Ylinebegin)+(0,-\txtdec@depth)$) rectangle
        ($(\Xend,\Yend)+(0,\txtdec@height-\txtdec@depth)$) ;
}


% using soul to set tikzmarks

\def\SOUL@tdleaders{%
  \stepcounter{txtdec@hyphmark}%
  \tikzmark{p\thepage.d\arabic{txtdec@decormark}.\arabic{txtdec@hyphmark}}%
  \leaders\hrule\@depth\z@\@height\z@\relax
}

\def\SOUL@tdunderline#1{{%
    \setbox\z@\hbox{#1}%
    \dimen@=\wd\z@
    \dimen@i=\SOUL@uloverlap
    \advance\dimen@2\dimen@i
    \rlap{%
      \null
      \kern-\dimen@i
      \SOUL@ulcolor{\SOUL@tdleaders\hskip\dimen@}%
      \hskip\dimen@
    }%
    \unhcopy\z@
  }}

\def\SOUL@tdpreamble{%
  \spaceskip\SOUL@spaceskip
  \setcounter{txtdec@hyphmark}{0}%
  \tikzmark{p\thepage.d\arabic{txtdec@decormark}.begin}%
}
\def\SOUL@tdeverysyllable{%
  \SOUL@tdunderline{%
    \the\SOUL@syllable
    \SOUL@setkern\SOUL@charkern
  }%
  \stepcounter{txtdec@hyphmark}%
  \tikzmark{p\thepage.d\arabic{txtdec@decormark}.\arabic{txtdec@hyphmark}}%
}
\def\SOUL@tdeveryhyphen{%
  \discretionary{%
    \unkern
    \SOUL@tdunderline{%
      \SOUL@setkern\SOUL@hyphkern
      \SOUL@sethyphenchar
    }%
    \stepcounter{txtdec@hyphmark}%
    \tikzmark{p\thepage.d\arabic{txtdec@decormark}.\arabic{txtdec@hyphmark}}%
  }{}{}%
}
\def\SOUL@tdeveryexhyphen#1{%
  \SOUL@setkern\SOUL@hyphkern
  \SOUL@tdunderline{#1}%
  \stepcounter{txtdec@hyphmark}%
  \tikzmark{p\thepage.d\arabic{txtdec@decormark}.\arabic{txtdec@hyphmark}}%
  \discretionary{}{}{%
    \SOUL@setkern\SOUL@charkern
  }%
}
\def\SOUL@tdpostamble{%
  % create an extra mark, vertically displaced, to create an exit condition for the last line
  \stepcounter{txtdec@hyphmark}%
  \raisebox{-5pt}{\tikzmark{p\thepage.d\arabic{txtdec@decormark}.\arabic{txtdec@hyphmark}}}%
  \tikzmark{p\thepage.d\arabic{txtdec@decormark}.end}%
  \stepcounter{txtdec@decormark}%
}
\def\SOUL@tdsetup{%
  \SOUL@setup
  \let\SOUL@preamble\SOUL@tdpreamble
  \let\SOUL@everysyllable\SOUL@tdeverysyllable
  \let\SOUL@everyhyphen\SOUL@tdeveryhyphen
  \let\SOUL@everyexhyphen\SOUL@tdeveryexhyphen
  \let\SOUL@postamble\SOUL@tdpostamble
}
\DeclareRobustCommand*\textdecor[1][]{%
  \csxdef{decor@tikz@style@\thetxtdec@decormark}{#1}%
  \csxdef{decor@fsize@\thetxtdec@decormark}{\f@size pt}%
  \SOUL@tdsetup\SOUL@}


% get the drawing done AtBeginShipout

\AtBeginShipout{%
  \AtBeginShipoutUpperLeft{%
    % getting the number of the next page
    \setcounter{txtdec@thenextpage}{\thepage}%
    \stepcounter{txtdec@thenextpage}%
    % if the current decoration occurs on this page, stay on it
    \iftikzmark{p\thepage.d\arabic{txtdec@decordraw}.\arabic{txtdec@hyphdraw}}{%
      \toggletrue{txtdec@stayonpage}}{}%
    \whileboolexpr{togl {txtdec@stayonpage}}{%
      \begin{tikzpicture}[remember picture, overlay]
        \setlength{\txtdec@height}{\csuse{decor@fsize@\thetxtdec@decordraw}}%
        \iftikzmark{p\thepage.d\arabic{txtdec@decordraw}.begin}{%
          % if current decor begins in current page, get coordinates
          \gettikzxy{(pic cs:p\thepage.d\arabic{txtdec@decordraw}.begin)}{\Xbegin}{\Ybegin}}{%
          % if current decor begins in previous page, set to top left of the page
          \gettikzxy{(current page.north west)}{\Xbegin}{\Ybegin}}%
        \iftikzmark{p\thepage.d\arabic{txtdec@decordraw}.end}{%
          % if current decor ends in current page, get coordinates
          \gettikzxy{(pic cs:p\thepage.d\arabic{txtdec@decordraw}.end)}{\Xend}{\Yend}}{%
          % if current decor ends in future page, set to bottom right of the page
          \gettikzxy{(current page.south east)}{\Xend}{\Yend}}%
        \ifdim\Ybegin=\Yend % the simplest case, a single line
          \txtdec@draw@all
          \stepcounter{txtdec@decordraw}%
        \else % current textdecor has a line break          
          \toggletrue{txtdec@stayondecor}%
          \whileboolexpr{togl {txtdec@stayondecor}}{%
            \gettikzxy{(pic cs:p\thepage.d\arabic{txtdec@decordraw}.\arabic{txtdec@hyphdraw})}{\Xlinebegin}{\Ylinebegin}%
            \edef\Xcurrent{\Xlinebegin}%
            \edef\Ycurrent{\Ylinebegin}%
            \edef\Xnext{\Xcurrent}%
            \edef\Ynext{\Ycurrent}%
            \toggletrue{txtdec@stayonline}%
            \whileboolexpr{togl {txtdec@stayonline}}{%
              \ifdim\Ycurrent=\Ynext
                \stepcounter{txtdec@hyphdraw}%
                % if the following tikzmark exists, we are at a page break
                \iftikzmark{p\arabic{txtdec@thenextpage}.d\arabic{txtdec@decordraw}.\arabic{txtdec@hyphdraw}}{%
                  \edef\Xcurrent{\Xnext}%
                  \edef\Ycurrent{\Ynext}%
                  \gettikzxy{(current page.south east)}{\Xnext}{\Ynext}
                  \togglefalse{txtdec@stayondecor}%
                }{% else, we remain on the same page
                  \iftikzmark{p\thepage.d\arabic{txtdec@decordraw}.\arabic{txtdec@hyphdraw}}{%
                    \edef\Xcurrent{\Xnext}%
                    \edef\Ycurrent{\Ynext}%
                    \gettikzxy{(pic cs:p\thepage.d\arabic{txtdec@decordraw}.\arabic{txtdec@hyphdraw})}{\Xnext}{\Ynext}}{}%
                }%
              \else
                \edef\Xlineend{\Xcurrent}%
                \edef\Ylineend{\Ycurrent}%
                % if we are on the first line of the current decoration
                \ifdim\Ylinebegin=\Ybegin
                  \txtdec@draw@begin
                \else
                  % if we are on the last line of the current decoration
                  \ifdim\Ycurrent=\Yend
                    \txtdec@draw@end
                    \stepcounter{txtdec@decordraw}%
                    \setcounter{txtdec@hyphdraw}{1}%
                    \togglefalse{txtdec@stayondecor}%
                  % if we are in a middle line of the decoration
                  \else
                    \txtdec@draw@middle
                  \fi
                \fi
                \togglefalse{txtdec@stayonline}%  
              \fi
            }%
          }%
        \fi
      \end{tikzpicture}%
      % if the beginning of the next decor does not exist in this page, leave this page
      \iftikzmark{p\thepage.d\arabic{txtdec@decordraw}.begin}{}{%
        \togglefalse{txtdec@stayonpage}}%
      % if the continuation of the current decor exists in the next page, leave this page
      \iftikzmark{p\arabic{txtdec@thenextpage}.d\arabic{txtdec@decordraw}.\arabic{txtdec@hyphdraw}}{%
        \togglefalse{txtdec@stayonpage}}{}% 
    }%
  }%
}

\makeatother

\begin{document}

\vspace*{5cm}

Lorem ipsum \textdecor{dolor sit amet, consectetuer adipiscing}
elit. Ut purus elit, vestibulum ut, placerat ac, adipiscing vitae,
felis. Curabitur dictum gravida mauris. Nam arcu libero, nonummy eget,
consectetuer id, vulputate a, magna. \textdecor[draw=green]{Donec
  vehicula augue eu neque. Pellentesque habitant morbi tristi-que
  senectus et netus et malesuada fames ac -- turpis ---
  egestas. Mauris ut leo. Cras viverra metus rhoncus sem. Nulla et
  lectus vestibulum urna fringilla ultrices.} Phasellus eu tellus sit
amet tortor gravida placerat. Integer sapien est, iaculis in, pretium
quis, viverra ac, nunc. Praesent eget sem vel leo ultrices
bibendum. Aenean faucibus. Morbi dolor nulla, malesuada eu, pulvinar
at, mollis ac, nulla. Curabitur auctor semper nulla. Donec varius orci
eget risus. Duis nibh mi, congue eu, accumsan eleifend, sagittis quis,
diam. Duis eget orci sit amet orci dignissim rutrum.  Nam dui ligula,
fringilla a, euismod sodales, sollicitudin vel, wisi. Morbi auctor
lorem non justo. Nam lacus libero, pretium at, lobortis vitae,
ultricies et, tellus. Donec aliquet, tortor sed accumsan bibendum,
erat ligula aliquet magna, vitae ornare odio metus a mi.  Morbi ac
orci et nisl hendrerit mollis. Suspendisse ut massa.  Nam dui ligula,
fringilla a, euismod sodales, sollicitudin vel, wisi. Morbi auctor
lorem non justo. Nam lacus libero, pretium at, lobortis vitae,
ultricies et, tellus. Donec aliquet, tortor sed accumsan bibendum,
erat ligula aliquet magna, vitae ornare odio metus a mi.  Morbi ac
orci et nisl hendrerit mollis. Suspendisse ut massa.  \textdecor[red,
draw=black]{Nam dui ligula, fringilla a, euismod sodales, sollicitudin
  vel, wisi. Morbi auctor lorem non justo. Nam lacus libero, pretium
  at, lobortis vitae, ultricies et, tellus. Donec aliquet, tortor sed
  accumsan bibendum, erat ligula aliquet magna, vitae ornare odio
  metus a mi.  Morbi ac orci et nisl hendrerit mollis. Suspendisse ut
  massa.  Nam dui ligula, fringilla a, euismod sodales, sollicitudin
  vel, wisi. Morbi auctor lorem non justo. Nam lacus libero, pretium
  at, lobortis vitae, ultricies et, tellus. Donec aliquet, tortor sed
  accumsan bibendum, erat ligula aliquet magna, vitae ornare odio
  metus a mi.  Morbi ac orci et nisl hendrerit mollis. Suspendisse ut
  massa.  Cras nec ante. Pellentesque a nulla.} Cum sociis natoque
penatibus et magnis dis parturient montes, nascetur ridiculus mus.
Aliquam tincidunt urna. Nulla ullamcorper vestibulum
turpis. Pellentesque cursus luctus mauris.

\begin{quotation}
  Lorem ipsum {\large \textdecor[blue]{dolor sit amet, consectetuer
      adipiscing}} elit. Ut purus elit, vestibulum ut, placerat ac,
  adipiscing vitae, felis. Curabitur dictum gravida mauris. Nam arcu
  libero, nonummy eget, consectetuer id, vulputate a, magna.
  \textdecor[green]{Donec vehicula augue eu neque. Pellentesque
    habitant morbi tristi-que senectus et netus et malesuada fames ac
    -- turpis --- egestas. Mauris ut leo. Cras viverra metus rhoncus
    sem. Nulla et lectus vestibulum urna fringilla ultrices.}
  Phasellus eu tellus sit amet tortor gravida placerat.
\end{quotation}

\begin{itemize}
\item Lorem ipsum \textdecor[blue, draw=magenta]{dolor sit amet,
    consectetuer adipiscing} elit.
\item Ut purus elit, vestibulum ut, placerat ac, adipiscing vitae,
  felis. Curabitur dictum gravida mauris. Nam arcu libero, nonummy
  eget, consectetuer id, vulputate a, magna. \textdecor[gray]{Donec
    vehicula augue eu neque. Pellentesque habitant morbi tristi-que
    senectus et netus et malesuada fames ac -- turpis ---
    egestas. Mauris ut leo.}
\end{itemize}

\end{document}

The overall setting is quite flexible, given we draw with tikz on a set of coordinates selected from the tikzmarks. Page breaks work fine (including multiple). The highlighting is drawn below the text, as mentioned before. And the decoration height is responsive to the current font size.

Applications at:

Ah! Important. Credits here also go to @marmot, for stimulus and ideas on chat.

gusbrs
  • 13,740
  • 1
    Sorry for the long silence. I have tried to make the TikZ parts of your code more tikzy but there is a problem with using TikZ styles ... and unfortunately I cannot post things to our chat because the chat room got frozen, do you know how to unfreeze it? –  May 20 '18 at 04:33
  • @marmot Unfrozen it is (by Stephan). – gusbrs May 20 '18 at 11:21
11

You are not showing if and how context can do what you want. But in any case there is a major difference: LaTeX uses as engine pdftex while context uses luatex. So moving a context solution to latex would mean moving to lualatex.

In the case of "soul": \hl in soul is basically and \ul (underline) with a large and colored rule. Tweaking soul isn't so difficult: The complete code is about 900 lines. The important part which defines the user command start at line 525. soul splits the input in tokens, syllables, spaces, hyphen and you have to tell it what to do with the pieces. In the case of \ul/\hl eg. \leaders are used. It can be quite fun to change this (in real life you shouldn't redefine the \ul-commands as this will affect \ul. You should define your own new soul command). You can see quite good the "syllables":

 \documentclass{article}
 \usepackage{soul,xcolor}
 \begin{document}
 \hl{highlight} text

 \makeatletter

 \def\SOUL@ulleaders{%
     \leaders\hbox{%
      \textcolor{green}{\rule{2pt}{2.5ex}}%
      \textcolor{red}{\rule{2pt}{2.2ex}}}\relax
 }

 \def\SOUL@uleveryspace#1{%
     \SOUL@ulcolor{%
         #1%
         \leaders\hrule\@depth 0pt\@height\SOUL@ulht
         \hskip\spaceskip
     }%
     \null
 }

 \hl{highlight text}

 \ul{test}
 \end{document}
azetina
  • 28,884
Ulrike Fischer
  • 327,261
  • I like this idea, but I think that would be difficult to use with my TiKz idea... maybe. If you did non random paths vertically and used the width of the current character, it MAY work, except that you need to know where the vpos of the previous character ended? – Andrew Starks Nov 26 '10 at 22:54
  • I think that what one could do is create a command like \hls and \hle which begin and end a highlight. These insert 0pt width characters. Then, you'd let TeX typset the paragraph and just prior to it laying a sentence down, scan for the (x,y) of the \hls and create your TiKz box, starting there. End at the \hle or the end of the line. If EOL, then insert \hls character at the beginning of the next line. I have no idea if any of that is reasonable, possible, the worst way in the world to do it, or the freakin' best idea anyone's ever had... – Andrew Starks Nov 26 '10 at 22:59
  • @Ulriche: I didn't see your answer, but my solution ended up working on a similar principle, by using soul's tokenizing ability. (As did testing my answer, so I really wish I had seen yours :-) ) @Andrew: Again, I didn't see your comment, but that's basically the idea I had, except I used soul to do the insertion and accomplished the "checking" via inserting even more TikZ zero-width characters. – Antal Spector-Zabusky Nov 27 '10 at 04:04
  • This hack might have some side effect on the underlining commands as well. – Olivier Feb 05 '23 at 15:41
  • 1
    @Olivier As every change of internal commands can have ;-). But beside this: the answer is 12 years old. Nowadays I would recommend to make sophisticated highlighting with lualatex and the lua-ul package. soul is often to fragile. – Ulrike Fischer Feb 05 '23 at 15:52
  • @UlrikeFischer: indeed. But I would also need something compatible with PDFLaTeX. – Olivier Feb 05 '23 at 16:09
8

I thought I had an easy solution to this. I was wrong. I knew that the xcolor package make it easy to color text using \color, and, though I hadn't used it, knew it bragged of being able to put a background color on text also using \colorbox. However, even though the documentation claims that \colorbox acts just like \color it doesn't. Here's my test code:

\documentclass{article}
\usepackage{xcolor}
\begin{document}
{\color{blue}{%
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent
tortor. Suspendisse scelerisque mi non risus.}} Duis vitae ligula eget
nunc fermentum malesuada. Class aptent taciti sociosqu ad litora
torquent per conubia nostra, per inceptos himenaeos.

\vspace{2em}

{\colorbox{yellow}{% highlighted part
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent
tortor. Suspendisse scelerisque mi non risus.}} Duis vitae ligula eget
nunc fermentum malesuada. Class aptent taciti sociosqu ad litora
torquent per conubia nostra, per inceptos himenaeos.
\end{document}

You can see that the highlighted portion (i.e. with the yellow bg color) is not a part of the paragraph anymore, I think because of the use of \leavevmode. Anyway, I've emailed the author with this observation and I'm interested to see what he says.

azetina
  • 28,884
bev
  • 1,669
  • 1
    \colorbox is - as the name indicates - a box. It works like \makebox: It puts its content on one line. – Ulrike Fischer Nov 26 '10 at 09:09
  • 1
    Not all boxes are limited to one line, e.g.\parbox. What confused me is that there is no mention of this in the documentation (that I saw). His paragraph says: " \colorbox takes the same argument form as \textcolor, but the color specifies the background color of the box." His comparison to \textcolor makes one think they work the same, just one is fg and one is bg. I'm a big believer in accurate documention, Ulrike. – bev Nov 26 '10 at 09:48
  • I know that there are boxes with more than one line. That's why I wrote "it works line \makebox. But even \parbox is rectangular and behaves not like a font switch like \color. The documentation of xcolor focus on the color declaration. It assumes that \colorbox itself is known. You find its description in the graphics documentation grfguide. – Ulrike Fischer Nov 26 '10 at 12:14
  • Not to be a smart-alec, but every single stinkin' thing in TeX is a box, including a single character. I would have gone down the same dead end as bev. – Andrew Starks Nov 26 '10 at 23:00
  • 1
    @Andrew: everything is either a vertical or horizontal box. You can't have a box that flows over lines. – Will Robertson Nov 27 '10 at 03:17
  • @Will - My original notion of \colorbox was that it put a bg color into each letter-box so the problem of flowing over lines would be moot (if that is possible - I don't know). Without taking anything away from Dr. Kern's wonderful xcolor package, I don't think I would have included \colorbox as it is without a very explicit caveat about it being limited to one line. I know that this is a quibble, but IMO this sort of fine-grained accuracy in documentation is important. The amount of time I've wasted in finding out things that IMO should have been documented is maddening to recall. – bev Nov 28 '10 at 02:01
  • @bev: If it were possible, it'd look bad, because different letter boxes have different heights and depths (e.g., o versus l versus g). I tend to agree with Ulrike and Will about the documentation, though I see where you're coming from; it's assuming a piece of TeXnical knowledge (boxes cannot break, and are thus set on one line) which you may or may not have. – Antal Spector-Zabusky Nov 28 '10 at 03:15
  • @AntalS-Z - I've known about non-breaking boxes for years, but that doesn't mean that if the documentation indicates, as it does, that the author has found a way to make the background color of text non-white I will automatically assume that it only means for one line, because of non-breaking boxes. I will, and did, assume that he figured out a way to make it work. Caramdir's example shows it can be done. Don't you assume that the author of a package did what his documentation claims, even if you don't know how it was accomplished? I do. – bev Nov 28 '10 at 10:29
  • @bev: As it happens, I assumed exactly the opposite thing on reading about \colorbox, though I admit that I can't remember if I read about it in the documentation or not originally. I think this discussion probably indicates that it would be worth throwing in a reference to \makebox or something like that, but I don't think it's inaccurate, just slightly unclear (it depends on what you read "the box" as referring to). And the "you" in "you may or may not have" was supposed to be a generic "you", not "you, bev" in particular. Sorry that that was unclear, I should have said "one". – Antal Spector-Zabusky Nov 28 '10 at 15:04
  • @bev I am not a LaTeX afficionado: it's just that it is what I need to use to write my stuff. So, given also that I am old, I agree completely with your "The amount of time I've wasted in finding out things that IMO should have been documented is maddening to recall." – schremmer May 19 '16 at 14:48