%luacensor.sty
\def\luacensorversionnumber{1.0.0}
\ProvidesPackage{luacensor}
[2022/02/18 \luacensorversionnumber\
 Redact sensitive information using Lua]
% !TeX program = lualatex                                   
% !TeX encoding = utf8
% 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 Elijah Z Granet
%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%
% DEPENDENCIES
%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%
\RequirePackage{luacode}
\RequirePackage{environ}% http://ctan.org/pkg/environ

\RequirePackage{verbatim}
% ^ for the censoring
\RequirePackage{accsupp}
%^for accessibility
\RequirePackage{fontspec}
%^for black lines
%in theory, you could do 
%a lighter version of this
%package with just asterisks 
%or `[TEXT-REDACTED]'
%And perhaps that's better for
%the environment with printing
%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%
% FONTS
%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%
% redacted is prettier and free to download
%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Strongly recommended
%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%
\IfFontExistsTF{Redacted}{%
\newfontface\cnsrfnt[%%%%%%
%the scale is arbitrary, but kind of works
Scale=1.1,
%%the below declarations are to prevent warnings about shapes not being available
ItalicFont=Redacted,%
BoldItalicFont=Redacted,%
BoldFont=Redacted,%
SmallCapsFont=Redacted]{Redacted}
\newcommand{\onething}{\cnsrfnt\ • }
\newcommand{\twothings}{\cnsrfnt\ • • }
\newcommand{\donothing}{}
%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%
%The little spaces let justification happen
%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%
% x chosen as an arbitrary average width
%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%
}{
%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%
% This option works perfectly
%fine, it's just less pretty
%%but a good fallback because
% Source Sans is in TeX dists by default
%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%
\newfontface\cnsrfnt[Scale=1.01,%To allow for separate use of source sans in text
WordSpace=0,%To make it all one black line
%the below declarations are to prevent warnings about shapes not being available
ItalicFont={Source Sans Pro Black},BoldItalicFont={Source Sans Pro Black},BoldFont={Source Sans Pro Black},SmallCapsFont={Source Sans Pro Black}]{Source Sans Pro Black}
%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Bit of unicode magic below to make the black line effect
%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%
\newcommand{\onething}{\cnsrfnt ▬ }
\newcommand{\twothings}{\cnsrfnt ▬ ▬ }
\newcommand{\donothing}{}
}

%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%
% A neat fallback for disappearing things…
%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%
% FULL CREDIT
% and FULSOME THANKS
% TO TEX.SE USER
% Werner  for the code below
%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%
\makeatletter
\newcommand{\voidenvironment}[1]{%
  \expandafter\providecommand\csname env@#1@save@env\endcsname{}%
  \expandafter\providecommand\csname env@#1@process\endcsname{}%
  \@ifundefined{#1}{}{\RenewEnviron{#1}{}}%
}
\makeatother
\newcommand{\hddn}[1]{%
\ifcnsr{}\else%
#1\fi}
\newenvironment*{hidden}{\begin{@empty}
}{\end{@empty}}
\voidenvironment{hidden}

%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%
% the CENSOR COMMAND
%%%%%%%%%%%%%%%%%%%%%%%%%%%

\newif\ifcnsr\cnsrfalse
\newcommand{\cnsr}[1]{%
  \ifcnsr{%

\voidenvironment{equation*}
\voidenvironment{equation}
\voidenvironment{table}
\voidenvironment{table*}
\voidenvironment{tabular}
\voidenvironment{tabular*}
\voidenvironment{}
%%%%%%%%%%%%%%%%%%%%%%%%%%%
% I don't know how many
% people use TEX native accent commands
% in LuaTEX given that using Unicode is more
%people's style.  But just in case, because these can lead to stray accent marks floating above censored letters.
%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%% 
%%%%%%%%%%%%%%%%%%%%%%%%%%%
\renewcommand{\`}[1]{}
\renewcommand{\'}[1]{}
\renewcommand{\^}[1]{}
\renewcommand{\"}[1]{}
\renewcommand{\H}[1]{}
\renewcommand{\~}[1]{}
\renewcommand{\c}[1]{}
\renewcommand{\k}[1]{}
\renewcommand{\l}[1]{}
\renewcommand{\=}[1]{}
\renewcommand{\b}[1]{}
\renewcommand{\.}[1]{}
\renewcommand{\d}[1]{}
\renewcommand{\r}[1]{}
\renewcommand{\u}[1]{}
\renewcommand{\v}[1]{}
\renewcommand{\t}[1]{}
\renewcommand{\o}[1]{}
\renewcommand{\i}[1]{}
%%%%%%%%%%%%%%%%%%%%%%%%%%%
%TEX primitives can break the
%code because they don't have the good manners
%to put their arguments in brackets
%No offence, of course, to the 
%honoured Prof Knuth, who had
%magnificent genius reasons 
%for coding things that way
%it's just that i'm an idiot
%and therefore need things simple
%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%% 
%%%%%%%%%%%%%%%%%%%%%%%%%%%
\renewcommand{\hskip}[1]{}
\renewcommand{\vskip}[1]{}
\renewcommand{\raise}[1]{}
\renewcommand{\lower}[1]{}
\renewcommand{\kern}[1]{}
% here we have the accsupp magic
% this operates by replacing the 'x's 
% or unicode black squares as the case
% may be with an alt text
% this serves a dual purpose of both making 
%pdftotext not break with huge strings of meaningless characters
%but more importantly
% it means screen readers don't subject
%. their users to the meaningless reading out of unicode black squares 50 times in a row!
%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%
\BeginAccSupp{method=plain,ActualText={TEXT REDACTED}}%
\rndstring{#1}
\EndAccSupp{}%
}
  \else%
%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%
% if the conditional is off
% the command does absolutely nothing
%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%
#1%
\fi}
%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%
% The LUA MAGIC PART
%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%
\begin{luacode}
--fulsome thanks to TeX.SE users Henri Menke and David Carlisle, without whom none of this would be possible 
local function rndstring()

    local toks = token.scan_toks(s)
        local on = true
for n, t in ipairs(toks) do
    if t.csname == "begin" or t.csname == "end"  then
       on = false
-- The below is necessary as TeX primitives can break the code otherwise because they do not use brackets       
    end
   
   if not(on) and t.cmdname == "right_brace" then
     on = true
     -- This prevents needless errors about gibberish up commands
end
if on and t.csname ==  "&" then
  local letter = token.create'donothing'
 toks[n] = letter

elseif on and t.csname == "%" then
  local letter = token.create'donothing'
 toks[n] = letter

elseif on and t.csname == "$" then
  local letter = token.create'donothing'
 toks[n] = letter

elseif on and t.csname == "#" then
  local letter = token.create'donothing'
 toks[n] = letter

elseif on and t.csname == "_" then
  local letter = token.create'donothing'
 toks[n] = letter

elseif on and t.csname == "{" then
  local letter = token.create'donothing'
 toks[n] = letter

elseif on and t.csname == "}" then
  local letter = token.create'donothing'
 toks[n] = letter

elseif on and t.csname == "~" then
  local letter = token.create'donothing'
 toks[n] = letter

elseif on and t.csname == "^" then
  local letter = token.create'donothing'
 toks[n] = letter
elseif on and t.cmdname ==  "letter" then
-- The below is the randomness part of this, which I admit is fairly arbitrary, but will more often  artificially shorten strings than lengthen them, as testing found if lengthening was too frequent, it led to really unsightly long strings. 
            local f = math.random (1,20)
            if f == 1 then
               local letter = token.create'donothing'
 toks[n] = letter

            	elseif f == 2 then
               local letter = token.create'donothing'
 toks[n] = letter
 elseif f == 3 then
   local letter = token.create'donothing'  toks[n] = letter
 elseif f == 4 then
               local letter = token.create'twothings'
  toks[n] = letter
   elseif f == 5 then
   local letter = token.create'donothing'  toks[n] = letter

   else 
               local letter = token.create'onething'
 toks[n] = letter
            	end
            	elseif
            on and t.cmdname == "spacer" then
            local f = math.random (1,20)
            	if f == 2 then
               local letter = token.create'donothing'
 toks[n] = letter
         		            elseif f == 3 then
                    
               local letter = token.create'donothing'  toks[n] = letter
 elseif f == 4 then
 local letter = token.create'donothing'
  toks[n] = letter
           		            elseif f == 5 then
                    

               local letter = token.create'twothings'  toks[n] = letter
 elseif f == 6 then
                    

               local letter = token.create'donothing'  toks[n] = letter
                  elseif f == 7 then
   local letter = token.create'donothing'  toks[n] = letter


   else 
               local letter = token.create'onething'
 toks[n] = letter
 
            	end

            	elseif
            on and t.cmdname == "other_char" then
            local f = math.random (1,20)
            	if f == 2 then
               local letter = token.create'donothing'
 toks[n] = letter
         		            elseif f == 3 then
                    
               local letter = token.create'donothing'  toks[n] = letter
 elseif f == 4 then
 local letter = token.create'donothing'
  toks[n] = letter
           		            elseif f == 5 then
                    

               local letter = token.create'twothings'  toks[n] = letter
 elseif f == 6 then
                    

               local letter = token.create'donothing'  toks[n] = letter
                  elseif f == 7 then
   local letter = token.create'donothing'  toks[n] = letter


   else 
               local letter = token.create'onething'
 toks[n] = letter
 
            	end
                    end
    end
 --Drop the token in and move on
        token.put_next(toks)
end
local lft = lua.get_functions_table()
--make a global command
lft[#lft + 1] = rndstring
token.set_lua("rndstring", #lft, "global")
\end{luacode}