Blackout censor macro capable of handling italic and bold fonts

macrosprogrammingspacingtikz-pgfunderline

In the following TeX stackexchange answer, the author provides an excellent \blackout macro that produces very aesthetically pleasing underline effect.

However, this macro does not work well with any bold or italic text. In particular, instead of

\blackout{This should be \textbf{bold} and \textit{italic}}

which fails with

! Argument of \textbf  has an extra }.

one has to use

\blackout{This should be} \textbf{\blackout{bold}} \blackout{and} \textit{\blackout{italic}}}

which is more verbose. Is there a way to adjust the definition of \blackout to handle this, or perhaps a way how to automatically apply \blackout to every word in the sentence, thereby automatically producing the more verbose version?

EDIT: I've figured out that using \bfseries ... \mdseries instead of \textbf{...} and \itshape ... \upshape instead of \textit{...} fixes the problem. I'd still like to understand why though, and if the original \blackout macro could be modified to work with \textbf{} / \textit{}..

Best Answer

I have been working on a revamped version of censor which uses token cycles to process the argument to \blackout and \xblackout. It can, at least, digest macros. There are still issues, as explained in the new manual, but in a case like the OP describes, it works just fine.

This will also allow alternate syntax of \blackoutenv ... \endblackoutenv. Likewise for \xblackoutenv.

\documentclass{article}
\usepackage{censor-PROVISIONAL}
\begin{document}
This should be \textbf{bold} and \textit{italic}

\blackout{This should be \textbf{bold} and \textit{italic}}

\xblackout{This should be \textbf{bold} and \textit{italic}}
\end{document}

enter image description here

Here's another example that highlights usage with expandable macros:

\documentclass{article}
\usepackage{censor-PROVISIONAL}
\begin{document}
Today's date is \today. Let it be great.

\blackout{Today's date is \today. Let it be great.}

\blackout{Today's date is \expanded{\today}. Let it be great.}
\end{document}

enter image description here

I am hoping to release it soon, but in the meantime, I will post a provisional version.

Here is censor-PROVISIONAL.sty

% censor.sty
\def\censorversionnumber{4.0}
\ProvidesPackage{censor-PROVISIONAL}
[2021/09/14 \censorversionnumber
 Provides capability for redaction of sensitive information]

%
% This work may be distributed and/or modified under the 
% conditions of the LaTeX Project Public License, either version 1.3 
% of this license or (at your option) any later version.
% The latest version of this license is in
%   http://www.latex-project.org/lppl.txt
% and version 1.3 or later is part of all distributions of LaTeX 
% version 2005/12/01 or later.
%
% This work has the LPPL maintenance status `maintained'.
%
% The Current Maintainer of this work is Steven B. Segletes.

% VERSION:
% 1.00 - Initial release
% 2.00 - Added \blackout
% 2.10 - Allowed \blackout to cross paragraph boundaries with use of
%        \bpar. Stopped censoring periods, in order to preserve
%        end-of-sentence spacing, which differs from inter-word spacing.
% 3.00 - \censorbox introduced to handle figures, tables, etc.
% 3.10 - Made \blackout work with \par in argument.  Introduced 
%        \xblackout
% 3.20 - Specify depth/height of censor rule.  Introduced \def\censordot{}
% 3.21 - Fixed bug regarding \xblackout rules remaining after a
%        \StopCensoring
% 3.22 - changed `\if to \ifx in definition of \bl@t, to handle macros like \%
% 4.0  - Recast \blackout and \xblackout in terms of tokcycle environments
%      - Introduced \blackoutenv...\endblackoutenv and 
%        \xblackoutenv...\endxblackoutenv
\usepackage{pbox}

\newlength\censorruledepth
\newlength\censorruleheight

\censorruledepth=-0.3ex% -0.3ex DEFAULT
\censorruleheight=2.1ex%  2.1ex DEFAULT
\def\censordot{\censor{.}}% versus \def\censordot{ }%

\newcommand\censorrule[1]{\protect\rule[\censorruledepth]{#1}{\censorruleheight}}

\newcommand\censor{\@ifstar{\@cenlen}{\@cenword}}
  \newcommand\@cenlen[1]{\censorrule{#1 ex}}
  \newcommand\@cenword[1]{\censorrule{\widthofpbox{#1}}}

\newcommand\un@censor{\@ifstar{\un@cenlen}{\un@cenword}}
  \newcommand\un@cenlen[1]{\protect\underline{\hspace{#1 ex}}}
  \newcommand\un@cenword[1]{#1}

\newcommand\StopCensoring{%
           \let\censor\un@censor%
           \let\censorbox\un@censorbox%
           \renewcommand\censpace{ }%
}
\newcommand\RestartCensoring{%
           \renewcommand\censor{\@ifstar{\@cenlen}{\@cenword}}%
           \renewcommand\censorbox{\@ifstar{\censor@dim}{\censor@box}}%
           \let\censpace\sv@censpace%
}

%%%%%%%%%%%%%%%%%%%%%%%%%%%

\usepackage{tokcycle}[2021/03/10]


\def\@dump#1{\addcytoks[1]{\expandafter\censor\expandafter{#1}}\def#1{}}
\def\@append#1#2{\tc@defx#1{#1#2}}

\def\spacelap{0.6ex}
\def\censpace{\rlap{\censorrule{\spacelap}} \llap{\censorrule{\spacelap}}}
\let\sv@censpace\censpace

\long\def\blackout#1{\blackoutenv#1\endblackoutenv}
\long\def\xblackout#1{\xblackoutenv#1\endxblackoutenv}

\newif\ifexpandarg

\xtokcycleenvironment\xblackoutenv
  {\ifx.##1\@dump\censored@word\addcytoks[1]{\censordot}\else
    \@append\censored@word{##1}%
    \tcpeek\@next\ifx\@next\@tcEscapeptr\@dump\censored@word\fi\fi}
  {\tctestifcon\ifexpandarg{\expandafter\processtoks\expandafter{\expanded{##1}}}%
    {\processtoks{##1}}\@dump\censored@word\expandargfalse}
  {\@dump\censored@word\tctestifx{~##1}{\@append\censored@word{##1}}%
    {\tctestifx{\expanded##1}{\expandargtrue}{\addcytoks{##1}}}}
  {\@dump\censored@word\addcytoks{\censpace}}
  {\def\censored@word{}}
  {\@dump\censored@word}
\xtokcycleenvironment\blackoutenv
  {\ifx.##1\@dump\censored@word\addcytoks[1]{\censordot}\else
    \@append\censored@word{##1}%
    \tcpeek\@next\ifx\@next\@tcEscapeptr\@dump\censored@word\fi\fi}
  {\tctestifcon\ifexpandarg{\expandafter\processtoks\expandafter{\expanded{##1}}}%
    {\processtoks{##1}}\@dump\censored@word\expandargfalse}
  {\@dump\censored@word\tctestifx{~##1}{\@append\censored@word{##1}}%
    {\tctestifx{\expanded##1}{\expandargtrue}{\addcytoks{##1}}}}
  {\@dump\censored@word\addcytoks{##1}}
  {\def\censored@word{}}
  {\@dump\censored@word}



%%%%%%%%%%%%%%%%%%%%%%%%%%

\newcommand\censorbox{\@ifstar{\censor@dim}{\censor@box}}
  \newcommand\censor@dim[4][]{{#1%
                      \rule[-#4\baselineskip]{#2ex}{#3\baselineskip}}}
  \newcommand\censor@box[2][]{#1\setbox0\hbox{#2}%
                      \rule[-\the\dp0]{\the\wd0}{\the\ht0+\the\dp0}}

\newcommand\un@censorbox{\@ifstar{\un@censor@dim}{\un@censor@box}}
  \newcommand\un@censor@dim[4][]{{#1%
                      \fbox{\rule[-#4\baselineskip]{0ex}{#3\baselineskip}
                      \rule{#2ex}{0ex}}}}
  \newcommand\un@censor@box[2][]{#1#2}

% NOTE: A \protect\censorbox{} MAY BE REQUIRED INSIDE SOME ENVIRONMENTS
\endinput

SUPPLEMENT

The OP was disappointed that the censor hack that worked in List of Underlining Packages - Pros and Cons no longer seems to work with this provisional package. An investigation reveals that what doesn't work isn't my censor hack, but my lipsum hack (to get expanded lipsum text) in that answer. The essential censor hack still works in this context:

\documentclass{article}
\usepackage{censor-PROVISIONAL}
\usepackage[none]{hyphenat}
\usepackage{lipsum}
\usepackage{xcolor}
\usepackage{stackengine}
\usepackage{scalerel}
\censorruledepth=-.25ex
\censorruleheight=.1ex
\newlength\maxkern
\setlength{\maxkern}{.1ex}
\newlength\nextcharwidth
\makeatletter
\renewcommand\@cenword[1]{%
  \setlength{\nextcharwidth}{\widthof{#1}}%
  \censorrule{\nextcharwidth}%
  \kern -\nextcharwidth%
  \color{white}%
  \kern -.5\maxkern #1\kern .5\maxkern%
  \kern -\nextcharwidth%
  \kern -\maxkern #1\kern \maxkern%
  \kern -\nextcharwidth%
  \kern .5\maxkern #1\kern -.5\maxkern%
  \kern -\nextcharwidth%
  \kern \maxkern #1\kern -\maxkern%
  \kern -\nextcharwidth%
  \color{black}%
  #1{}}
\begin{document}
Thiys, should pe \textbf{bold} ang \textit{italic}!

\blackout{Thiys, should pe \textbf{bold} ang \textit{italic}!}

\xblackout{Thiys, should pe \textbf{bold} ang \textit{italic}!}
\end{document}

enter image description here