# Censoring Curse Words with Grawlixes

Suppose that I would like to censor a curse word <word> using grawlixes, e.g. "What the #@$! are you doing?!" How can I define a command \censor such that \censor{<word>} results in a string of these symbols? I am not entirely sure which or how many symbols should be used given the word <word>. I suppose that the number of symbols used should approximately equal the number of characters in <word>. Thanks for your input! • It is much easier to do that in the text editor source than in TeX. (luatex is probably easier than classical tex if you really want to do it at the text level) (It is easy enough to do it in restricted text contexts but hard if you need to do it in arbitrary paragraphs and lists etc) – David Carlisle Jun 5 '13 at 22:53 • – David Carlisle Jun 5 '13 at 22:55 • @percusse I #@$!&%? !%$@ !%$. – Gonzalo Medina Jun 5 '13 at 23:13
• @GonzaloMedina Can I buy a vowel? – Qrrbrbirlbel Jun 6 '13 at 4:32
• Well, at least I learned a new word today. But why isn't the plural grawlices? – Matthew Leingang Jun 6 '13 at 12:39

A random symbol is taken from a list and appended to a token list; when the width of the accumulated symbols is more than the width of the word minus 2pt, the symbols are printed, otherwise another symbol is appended.

\documentclass{article}
\usepackage{xparse,pgf}

\ExplSyntaxOn
\NewDocumentCommand{\censor}{m}
{
\pointer_censor:n { #1 }
}

\seq_new:N \g_pointer_grawlixes_seq
\tl_map_inline:nn { @ * \# ! \$\% ? ! \# @ \% *} { \seq_gput_right:Nn \g_pointer_grawlixes_seq { #1 } } \int_const:Nn \c_pointer_grawlix_list_int { \seq_count:N \g_pointer_grawlixes_seq } \dim_new:N \l_pointer_censor_dim \dim_new:N \l_pointer_try_dim \box_new:N \l_pointer_censor_box \tl_new:N \l_pointer_grawlixes_tl \cs_new_protected:Npn \pointer_censor:n #1 { \tl_clear:N \l_pointer_grawlixes_tl \hbox_set:Nn \l_pointer_censor_box { #1 } \dim_set:Nn \l_pointer_censor_dim { \box_wd:N \l_pointer_censor_box } \pointer_add_grawlix: } \cs_new_protected:Npn \pointer_add_grawlix: { \hbox_set:Nn \l_pointer_censor_box { \l_pointer_grawlixes_tl } \dim_compare:nTF { \l_pointer_censor_dim - 2pt < \box_wd:N \l_pointer_censor_box } { \tl_use:N \l_pointer_grawlixes_tl } { \pgfmathparse{random(1,\int_eval:n {\c_pointer_grawlix_list_int})} \tl_put_right:Nx \l_pointer_grawlixes_tl { \seq_item:Nn \g_pointer_grawlixes_seq { \pgfmathresult } } \pointer_add_grawlix: } } \ExplSyntaxOff \begin{document} Censored \censor{Censored} \end{document}  A humble attempt with lualatex: The replacement code is very naive, we would need something more robust. \documentclass{article} \directlua{ % my list of bad words bad_words = { "fish", "cat", "dog", "horse", "alligator" } % the replacement string replacement = "duck" % a replacement function which returns % both the altered line and the number % of occurrences function replace(line) for _, element in pairs(bad_words) do if string.find(line, element) then return string.gsub(line, element, replacement) end end return line, 0 end % my "naive" censor function, it simply % replaces any occurrences of the % list of bad words by the replacement % string function censor(line) occurrences = 0 repeat line, occurrences = replace(line) until occurrences == 0 return line end % add the hook callback.register('process_input_buffer', censor)} \begin{document} Once upon a time, there was a little cat who lived inside an igloo. Don't ask me what he was doing there. One day, the cat was visited by his two other friends, the dog and the alligator! --- What are you guys doing here?'', said the cat. --- We came to visit you, mr.\ cat!'', said the dog. --- Our friend horse will be late, he went to the store to buy some frozen fish for you, replied the alligator. \end{document}  The output: Moral of the story: I'm terrible at telling stories. :) Now, let's add the grawlixes. Since I need a better Lua code, let's create an external file censor.lua and call it from out .tex code: \begin{filecontents*}{censor.lua} -- a list of symbols to represent the -- grawlixe symbols -- note that we need to escape -- some chars grawlixe_symbols = { "\\$", "\\#", "@", "!", "*", "\\&" }

-- generate a grawlixe of length s
-- note that the seed is not so random, so
-- same values of s might get the same
-- grawlixe pattern (I could add another seed
-- mid code, but I'm lazy)
function grawlixe(s)
math.randomseed(os.time())
local u = table.getn(grawlixe_symbols)
local i = math.random(u)
local r = grawlixe_symbols[i]
local current
local w = 1
repeat
current = math.random(u)
while current == i do
current = math.random(u)
end
i = current
r = r .. grawlixe_symbols[i]
w = w + 1
until w == s
return r
end

-- a list of bad words to be censored
bad_words = { "fish", "cat", "dog", "horse", "alligator" }

-- our replacement function, it returns
-- the new line and the number of
-- note that this is a very naive replacement
-- function, there's a lot of room for
-- improvement
function replace(line)
for _, element in pairs(bad_words) do
if string.find(line, element) then
return string.gsub(line, element, grawlixe(string.len(element)))
end
end
return line, 0
end

-- the censor function, it repeats
-- ad nauseam until the line has
-- nothing more to be censored
function censor(line)
local occurrences = 0
repeat
line, occurrences = replace(line)
until occurrences == 0
return line
end

-- register the callback
callback.register('process_input_buffer', censor)
\end{filecontents*}

\documentclass{article}

\directlua{dofile('censor.lua')}

\begin{document}

Once upon a time, there was a little cat who lived inside an igloo. Don't ask me what he was doing there.

One day, the cat was visited by his two other friends, the dog and the alligator!

--- What are you guys doing here?'', said the cat.

--- We came to visit you, mr.\ cat!'', said the dog.

--- Our friend horse will be late, he went to the store to buy some frozen fish for you, replied the alligator.

\end{document}


The output:

Moral of the new story: adding grawlixes to a text makes it look naughty. :)

• +1 (even though I hoped to see a duck picture, perhaps it was censored?) – David Carlisle Jun 6 '13 at 11:21

Leaving aside the pros and cons of how and why to do this, I find it a nice little exercise to do with xstring. So here's my take on it:

\documentclass{article}

\usepackage{xstring}

\def\grawlix{{\makeatletter@\makeatother}\textdollar{$\sharp$}*?!}

\newcommand{\censor}[1]{\StrLen{#1}[\result]%
\expandarg\StrLeft{\grawlix}{\result}[]}

\begin{document}
What the \censor{word} is this???
\end{document}


As you can see, the limitation is that the grawlix will be composed of the same set of characters, in the same order. (You may redefine the character set or find a way to randomize them at each invocation of the macro... but that's a different story! ;)) But your grawlix will be as long as the censored word.

What happens is that xstring has his own ways of expanding arguments and the macros cannot be nested. So the result from the macro that finds the length of the censored word is returned in a different macro (imaginatively called here \result) that will be (re)used to split the predefined \grawlix at the right spot.

The \expandarg macro makes sure the expansion is done properly. According to xstring documentation, it will allow all arguments passed to be expanded exactly once. Therefore, care must be taken to protect groups that represent a single character with braces (e.g. the \sharp symbol lives in mathmode, but we don't want the dollar signs delimiting it to be seen as separate tokens).

• I don't think \makeatletter and \makeatother are necessary around the @... It can be used as the symbol it is right away. – clemens Jun 6 '13 at 8:13
• @cgnieder It's a good job they are not necessary as they have no effect at all if used inside the definition as here:-) – David Carlisle Jun 6 '13 at 8:43
• @DavidCarlisle true, I hadn't thought about that... I need more coffee – clemens Jun 6 '13 at 8:46

A plain TeX solution without additional packages, based on ideas of Kees van der Laan.

\documentclass{article}

\begin{document}

\newcount\cur
\newcount\cura

\def\fifo#1{\ifx\ofif#1\ofif\fi
\process#1\fifo}
\def\ofif#1\fifo{\fi}

\def\mynum#1{\cur\the\lccode#1\relax\the\cur }

\def\process#1{\cur\the\lccode#1\relax\cura\cur \divide\cura by7 \multiply\cura by7
\advance\cur by-\cura\ifcase\cur!\or@\or\#\or\$\or\%\or\&\or*\else ERROR\fi } \fifo censored \ofif \bigskip \def\censor#1{\fifo #1\ofif} \censor{fuck} \censor{WORD} it is \censor{shit} \end{document}  You probably want to have the result connected with the word used. In this solution it is \lccode of a character modulo 7. You can write either  \fifo censored \ofif  or, as suggested (sorry for my language), \censor{fuck} \censor{WORD} it is \censor{shit}  obtaining Some explanations. The main tool is the implemantation of FIFO (First-In-First-Out) queue suggested by Kees van der Laan: \def\fifo#1{\ifx\ofif#1\ofif\fi \process#1\fifo} \def\ofif#1\fifo{\fi}  The \fifo command calls a macro \process that handles the individual arguments. Every token is processed until \ofif. The rest is simple. Manipulating on the counters \cur and \cura we obtain a number between 0 and 6 and \ifcase takes different censor characters for different values. Remark. I am very interested of the real rules (if they exist) of replacing curse words by strings of @-like symbols. • Very nice. Could you perhaps expand (pardon the pun) a bit on how the code actually works? – Alan Munn Jun 6 '13 at 3:47 • @AlanMunn Yes, but after returning home (at late evening). – Przemysław Scherwentke Jun 6 '13 at 3:53 • \censor{MSWORD} – marczellm Jun 6 '13 at 13:05 A stable way to exchange symbols is to change the fontencoding or to reencode the font. A simple example \documentclass{article} \begin{document} \newcommand\censor[1]{\fontencoding{OMS}\selectfont #1} \censor{Censored} \end{document}  With lualatex it will in the future (when the interface is more stable) be probably be possible to create virtual reencoded fonts on-the-fly, currently it should be already possible to do it with a feature file. This is an extension to @egreg's answer using another answer by @egreg. With this code you can enter a whole sentence in \censor and all words initially added to a list using \addcensor are replaced by grawlixes. The argument to \addcensor can be either a single word or a comma separated list. \documentclass{article} \usepackage{xparse,pgf} \ExplSyntaxOn \NewDocumentCommand{\censor}{m} { \pointer_badseq:n { #1 } } \tl_new:N \g_pointer_badwords_tl \NewDocumentCommand{\addcensor}{m} { \clist_map_inline:nn { #1 } { \tl_gput_right:Nn \g_pointer_badwords_tl { {##1}{} } } } \cs_generate_variant:Nn \str_case:nnTF { nV } \cs_new_protected:Npn \pointer_badseq:n #1 { \seq_set_split:Nnn \l_tmpa_seq { ~ } { #1 } \seq_map_inline:Nn \l_tmpa_seq { \str_case:nVTF { ##1 } \g_pointer_badwords_tl { \pointer_censor:n { ##1 } } { ##1 } ~ % Readd space } \tex_unskip:D % Remove the trailing space } % From @egreg's answer \seq_new:N \g_pointer_grawlixes_seq \tl_map_inline:nn { @ * \# ! \$ \% ? ! \# @ \% *}
{
\seq_gput_right:Nn \g_pointer_grawlixes_seq { #1 }
}
\int_const:Nn \c_pointer_grawlix_list_int { \seq_count:N \g_pointer_grawlixes_seq }

\dim_new:N \l_pointer_censor_dim
\dim_new:N \l_pointer_try_dim
\box_new:N \l_pointer_censor_box
\tl_new:N \l_pointer_grawlixes_tl

\cs_new_protected:Npn \pointer_censor:n #1
{
\tl_clear:N \l_pointer_grawlixes_tl
\hbox_set:Nn \l_pointer_censor_box { #1 }
\dim_set:Nn \l_pointer_censor_dim { \box_wd:N \l_pointer_censor_box }
}
{
\hbox_set:Nn \l_pointer_censor_box { \l_pointer_grawlixes_tl }
\dim_compare:nTF
{ \l_pointer_censor_dim - 2pt < \box_wd:N \l_pointer_censor_box }
{
\tl_use:N \l_pointer_grawlixes_tl
}
{
\pgfmathparse{random(1,\int_eval:n {\c_pointer_grawlix_list_int})}
\tl_put_right:Nx \l_pointer_grawlixes_tl
{ \seq_item:Nn \g_pointer_grawlixes_seq { \pgfmathresult } }
}
}
\ExplSyntaxOff


• Definetly better (IMO). I like this way, even now that I'm reading ConTeXt source code and it writes completely different (at least they don't use the opening brace, new line, code, new line closing brace without indentation too much, but something more like %, new line, opening brace and code, more code, and closing brace next to the code). – Manuel Sep 13 '14 at 18:23