# Text highlighting that works with CMYK xcolor mode?

I looked at Cool Text Highlighting in LaTeX, and as my MWE (below) demonstrates:

... the first part, highlighted with soul's \hl, fails when [cmyk]{xcolor} is used. The accepted answer in the link above uses tikz, and that solution does work with CMYK - but I was quite irritated by the faded text issue, which I managed to only partially solve.

Basically, I would like highlighting that works with CMYK, multiple hyphenated lines in a paragraph, and does not fade the text (the highlight is drawn in the "background"/"behind" the text). If there is an easier way for that, great - else, here is some discussion of the modified tikz approach in the MWE, in case it can be changed so it works fully.

The first idea is that since that approach also uses soul's machinery to get syllables upon their way to hyphenation, we could insert \phantoms for the syllables when soul runs for a highlight - and at the same time concatenate/append the syllables into a partial sentence, that we'll render on top of the highlight (which necessarily would have to be drawn over the phantoms) - and so avoid the faded text issue.

The problem is: if you give soul something \boxed, like a \phantom, then the rest of the hyphenation/line breaking engine ends up wrong; the same happens if you try to draw white letters, e.g. via \textcolor{white}{#text}. So, we have to print the syllables in the first run; then the only thing to do is to make the highlight background opaque (so it covers the first run letters), and then print the concatenated sentence on top of it. As seen above, this is not all that bad of an approach - but, the debug version (in a gist) test.tex which uses lua-visual-debug (and transparent highlight background) reveals a bit more:

Note that the "double" letters on top of each other tend to mess up the selection process in a PDF reader (at least they do in evince - but somewhat strangely, that is not as pronounced when the highlight is opaque?!)

Basically, the overlay text is placed in a tikz node, which is "stretched" to cover the tikz highlight background (a \fill rectangle), and the text align= is then set to justify to stretch the text inside the node; clearly, this does not always result with the same glue sizes being generated for the respective spaces. Second, when the TeX engine decides to hyphenate and break the end of line, we don't get the respective hyphenation - in a soul handler; and so that one is only included in the first-run text, and so is covered by the opaque background.

This MWE uses concatenation of syllable content into a command/macro variable; however, in the debug code there is also a test which concatenates into a box (see also Accessing child contents of LaTeX boxes (append/concatenate TeX box material)?). The problem there is that the boxes always end up with a .\pdfcolorstack 0 pop at end of the box list, which is a problem because if the partial sentence ends with a space, it will mess up the sizing of the overlay text - and because of the .\pdfcolorstack, we cannot use \unskip to remove such trailing glues (spaces) from the box list (when using a "string" "variable", \unskip at end of the string will help removing a possible trailing space).

Here is the code:

% see also: https://gist.github.com/anonymous/999426bbae600abb9e74

\documentclass{book}
\usepackage[cmyk]{xcolor}
\usepackage{soulutf8}
\usepackage{trace}
\usepackage{tikz}
\usetikzlibrary{calc}
\usetikzlibrary{fit}

\usepackage{etoolbox} % \providerobustcmd
\makeatletter
\gdef\highlight@Tcontent{}
\global\newsavebox{\highlight@box}

\newcommand{\defhighlighter}[3][]{%
\tikzset{every highlighter/.style={color=#2, fill opacity=#3, #1}}%
}
% \defhighlighter{yellow!30}{.5} % transparent, to show the text below
\defhighlighter{yellow!30}{1} % fully solid/opaque, to hide the text below

\newcommand{\highlight@DoHighlight}{
\path let
\p1=($(begin highlight)+(0,8pt)$),
\p2=($(end highlight)+(0,-3pt)$),
\p3=($(\p2)-(\p1)$)
in \pgfextra %
\global\highlight@previous=\x3 %
\endpgfextra %
[fill,%
every highlighter,%
this highlighter,%
]%
(\p1) rectangle (\p2) ;%
\unskip\ignorespaces%
\node%
[anchor=south west,inner sep=0pt, outer sep=0pt, minimum size=0pt,
% hand tuned for best position:
fit={($(begin highlight)+(0pt,0pt)$)($(end highlight)+(0,-2pt)$)},
% align goes after fit;
% https://tex.stackexchange.com/questions/213105/forcing-left-text-alignment-in-a-fit-tikz-node
text width=\highlight@previous, %if it is set, align=left may break, align=justify doesn't
align=justify, %
]%
%at ($(begin highlight)+(0,2.5pt)$) % don't use at() if using fit!
{\ignorespaces\highlight@Tcontent\unskip\ignorespaces}%
;
\gdef\highlight@Tcontent{}% erase here for next round
}
\newcommand{\highlight@BeginHighlight}{
\coordinate (begin highlight) at (0,0) ;
}
\newcommand{\highlight@EndHighlight}{
\coordinate (end highlight) at (0,0) ;
}
\def\highlight@appendTcontent#1{%
% do the append to a "string"
\xdef\highlight@Tcontent{\highlight@Tcontent#1}%
% output the syllable characters:
#1%
% \hbox{#1}, \phantom{#1} makes main line breaking calc wrong ;
% an outer box probably messes up hyphenation algorithm
% (in #1 we get the syllable, but not the '-' at end if it gets hyphenated)
}
\newdimen\highlight@previous %y!
\newdimen\highlight@current %y!s
\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}% don't output raw anymore; only append it to the box
\highlight@appendTcontent{\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%
%\gdef\highlight@Tcontent{}%
\highlight@BeginHighlight%
\fi%
\end{tikzpicture}%
%\the\SOUL@syllable% don't output raw; only append it to the box
\highlight@appendTcontent{\the\SOUL@syllable}%
\tikz[overlay, remember picture] \highlight@EndHighlight ;%
}%
% must define for spaces too:
\def\SOUL@everyspace##1{%
%##1\space% "The macro should at least do {#1\space}."% don't output raw; only append it to the box
\highlight@appendTcontent{##1\space}%
}
%
\unskip\ignorespaces% this to avoid doubling spaces at start!
%
\SOUL@%
}
\makeatother

\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.}

% the highlights will render wrong if there is no space before them!

Lorem ipsum and the again %
\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.

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.

\end{document}


The following solution provides a workaround to the inability to use \usepackage[cmyk]{xcolor} to highlight text using the soul package.

xcolor provides for defining the core colour model either as a class option to xcolor (for example: \usepackage[rgb]xcolor). However, using \usepackage[cmyk]xcolor breaks the text highlighting capabilities of soul.

Another option for specifying the core color model is to incorporate it in the colour definitions. This is done using \definecolor[type]{name}{model-list}{spec-list} where <model-list> defines the colour model which can be written as as <core model>:model1/model2 etc. This format provides the ability to define the core model as rgb and the color specifications in cmyk (see section 2.3.1 of the xcolor manual).

Using this approach, the MWE steps though several examples that demonstrate correct and incorrect renderings of the svgnames color CornflowerBlue. The test replicates the well known problem that defining colour with cmyk may not render the colour correctly (see examples 4 and 5 below). The test demonstrates that the correct syntax for using a cmyk colour specification to the highlighting command from soul is to use \definecolor{colourname}{rgb:cmyk}{spec-list}.

The MWE then applies the same solution to the problem described by the OP, using \definecolor{rred}{rgb:cmyk}{0,1,1,0} and \definecolor{yyellow}{rgb:cmyk}{0,0,1,0} to provide the red and yellow highlighting respectively.

The MWE uses @Gumbo's \hlc command from here: Cool Text Highlighting in LaTeX.

\documentclass[a4paper,12pt]{article}
\usepackage[lmargin=2.5cm,rmargin=2.5cm,tmargin=2.5cm,bmargin=2.5cm]{geometry}
\usepackage[dvipsnames,svgnames]{xcolor}
\usepackage{soul}

\setlength{\parindent}{0pt}
\setlength{\parskip}{6pt}

% CornflowerBlue definitions
\definecolor{CB}{RGB}{100,149,237}
\definecolor{cba}{rgb}{0.392,0.584,0.929}
\definecolor{cbb}{cmyk}{0.58,0.37,0,0.07}
\definecolor{cbc}{cmyk:rgb}{0.392,0.584,0.929}
\definecolor{cbd}{rgb:cmyk}{0.58,0.37,0,0.07}

% colour defintions for red and yellow
\definecolor{rred}{rgb:cmyk}{0,1,1,0}
\definecolor{yyellow}{rgb:cmyk}{0,0,1,0}

\newcommand{\hlc}[2][yellow]{ {\sethlcolor{#1} \hl{#2}} }

\begin{document}
\begin{enumerate}
\item \textbf{CornFlowerBlue (an svgname) - correct}

\hlc[CornflowerBlue]{Lorem ipsum dolor sit amet, consectetur adipiscing elit.}

\item \textbf{\{RGB\}\{100,149,237\} - correct}

\hlc[CB]{Lorem ipsum dolor sit amet, consectetur adipiscing elit.}

\item \textbf{\{rgb\}\{0.392,0.584,0.929\} - correct}

\hlc[cba]{Lorem ipsum dolor sit amet, consectetur adipiscing elit.}

\item \textbf{\{cmyk\}\{0.58,0.37,0,0.07\} - incorrect}

\hlc[cbb]{Lorem ipsum dolor sit amet, consectetur adipiscing elit.}

\item \textbf{\{cmyk:rgb\}\{0.392,0.584,0.929\} - incorrect }

\hlc[cbc]{Lorem ipsum dolor sit amet, consectetur adipiscing elit.}

\item \textbf{\{rgb:cmyk\}\{0.58,0.37,0,0.07\} - correct }

\hlc[cbd]{Lorem ipsum dolor sit amet, consectetur adipiscing elit.}

\end{enumerate}

\hlc[yyellow]{Highlighting} text feels good.
You can draw attention of people to a \hlc[yyellow]{word} or perhaps
\hlc[yyellow]{even a whole sentence that spans across multiple lines
in such a way that hyphenation etc. are not affected.}

% the highlights will render wrong if there is no space before them!

Lorem ipsum and the again %
\hlc[rred]{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
\hlc[rred]{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.

Lorem ipsum %
\hlc[rred]{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
\hlc[rred]{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.

\end{document}


Note that this solution breaks when \usepackage[cmyk]{xcolor} is used and when \setcolormodel is used.

• You don't need color package right? – percusse Nov 6 '16 at 16:39
• @percusse. Correct. Color is obsolete with the inclusion of xcolor. Should I remove it? – Ross Nov 6 '16 at 16:42
• I guess you might remove it since people generally copy/paste solution and stick to it for a long time – percusse Nov 6 '16 at 16:43