[Tex/LaTex] How to hide (or blur) some code in a listing

listingsMATLAB

I have a code listing to write in a report, but I'd like to blur or hide some code parts with a colored box (of same length as the hidden text), so that readers cannot see the hidden parts. (Another requirement is that readers should not be able to copy & paste the hidden text from the PDF.)

As an example, I would like b=c; in the listing below to be hidden by a black box (or blurred).

For that, I define a \confidentiel for hiding text in a black box.
It works fine on normal text ouside a listing block. However, it doesn't work inside an mcode listing block: instead of producing a black box, \confidentiel{code to hide or blur} gets printed verbatim.

I'm using the mcode package to display Matlab code, but I suppose the listings package (rather than mcode) is to blame here, because mcode uses the listings package; the problem likely occurs no matter which listings language is used.

Any idea?

enter image description here

\documentclass{article}

\usepackage{tikz} %to be used with package below:
\usepackage[confidentiel]{optional}
\newlength\heightconf
\newlength\widthconf
\newcommand\confidentiel[1]{%
\opt{confidentiel}{%
 \settoheight{\heightconf}{#1}%
 \settowidth{\widthconf}{#1}%
 \tikz{\node[inner sep=0pt,rectangle,draw,anchor=base,text height=\heightconf,text width=\widthconf,fill=black]{};}%
}%
\opt{libre}{#1}}

% package to display matlab code:

\usepackage[framed,numbered,autolinebreaks,useliterate]{mcode}

%starting document 
\begin{document}

  \confidentiel{this text is hidden with black box}
  this text is not hidden

    \begin{lstlisting}
    for i=1:3 do 
        a=b;
        \confidentiel{b=c;}
        c=d
    end
     \end{lstlisting}
% the code above does not hide b=c; but
% prints literaly \confidentiel{b=c} but i don't want that !

\end{document}

Best Answer

Here, all code is shown:

enter image description here

Here, some code is hidden:

enter image description here

Here are some implementation details.

Invoke the "ghosts" of Martin Scharrer and Donald Arseneau

The \confidentiel macro defined below is based on a modified version of Martin Scharrer's \UL@putbox, which is itself a "phantom" version of a macro defined in Donald Arseneau's ulem package.

Such a macro applies cleanly over multiple lines, both inside and outside listings, and will prevent your readers from copying & pasting the hidden parts, no matter which PDF viewer they use.

Indulge yourself

Instead of escaping to LaTeX only to invoke your \confidentiel macro, the following approach defines delimiters that apply \confidentiel to what they delimit.

The backtick character, `, is an adequate (opening and closing) delimiter here, because it's unlikely to occur in Matlab code (it's only valid in Matlab string literals and system commands, nowhere else).

The main benefits of this approach is that hiding/unveiling stuff, either locally or globally, is fast and easy.

  • Hiding/unveiling stuff locally only requires adding/removing two backticks (`...`), as opposed to adding/removing \confidentiel{...} each time;
  • Hiding/unveiling stuff globally can be done by at the flip of the \ifconfid switch (comment/uncomment the line \confidtrue).

Use matlab-prettifier

I recommend using the matlab-prettifier package instead of mcode... but, as the author of the former, I'm somewhat biased :p Have a look at this answer, and see if you like matlab-prettifier enough to migrate from mcode.

\documentclass{article}

\usepackage[T1]{fontenc}
\usepackage{matlab-prettifier}
\usepackage[normalem]{ulem}

\newif\ifconfid
\confidtrue                   % comment this line out to unveil all hidden text
\colorlet{confidcolor}{black} % change the colour of boxes here (e.g. "red")


% ----------- implementation details -----------
\makeatletter

% from Martin's Scharrer's answer (https://tex.stackexchange.com/a/16004/21891)
\def\confid@UL@putbox{%
  \ifx\UL@start\@empty%
  \else % not inner
    \vrule\@width\z@ \LA@penalty\@M
    {\UL@skip\wd\UL@box \UL@leaders \kern-\UL@skip}%
    \phantom{\box\UL@box}%
  \fi
}

\newcommand\confidentiel{}%

\ifconfid
\renewcommand\confidentiel[1][confidcolor]{%
 \bgroup%
 \let\UL@putbox\confid@UL@putbox%
 \markoverwith{\hbox to.01em{\hss\textcolor{#1}{|}\hss}}\ULon%
}
\fi

\lst@AddToHook{Init}{%
  \ifconfid%
    \lstset{moredelim=[is][\confidentiel]``,keepspaces}%
  \else
    \lstset{moredelim=**[is][]``}%
  \fi
}
\makeatother
% ----------- end of implementation details -----------


\lstset{
  style=Matlab-editor,
  basicstyle=\mlttfamily,
}

\begin{document}
\confidentiel{%
    this text is hidden\\
    this text is hidden\\
    this text is hidden\\
}%
\xout{test that \textsf{ulem} works}
\begin{lstlisting}
for i=1:3
  a=b;
  % Guess what's next...
  `b=c;`
  c=d;
end
`% this text is hidden
% this text is hidden
% this text is hidden
`
\end{lstlisting}

\end{document}
Related Question