%luacensor.sty \def\luacensorversionnumber{1.1.1} \ProvidesPackage{luacensor} [2024/04/06 \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 %%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%% % option (we'll come back % to this later %%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%% \newif\ifwarning \warningfalse \DeclareOption{warning}{\warningtrue} \ProcessOptions* %%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%% % 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 would be better for %the environment with printing %BUT I MADE MY CHOICE! \RequirePackage{xcolor} \RequirePackage{graphicx} %%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%% % 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 %WordSpace=0, ItalicFont={Redacted-Regular.ttf},% BoldItalicFont={Redacted-Regular.ttf},% BoldFont={Redacted-Regular.ttf},% SmallCapsFont={Redacted-Regular.ttf}]{Redacted-Regular.ttf} \newcommand{\onething}{\cnsrfnt\ • } \newcommand{\twothings}{\cnsrfnt\ • •} \newcommand{\donothing}{\cnsrfnt\ } %%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%% %The little spaces let justification happen %%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%% % • 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]{}% %%%%%%%%%%%%%%%%%%%%%%%%%%%d %%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%% % 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} %%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%% % WARNING FUN YAY %%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%% % Definitely this whole section %is there to be user modified, because % depending on language, jurisdiction %type of document etc, everyone will need %a specific warning style. So the important % part of the code here is the % conditional and global [warning] % option, because that's the magic value added %%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%% % fonts for the warning: %I chose default LaTeX fonts % here to be changed as users wish %%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%% \newfontface\wrnstncl{QT Military} \newcommand{\warnword}{WARNING} \newfontface\smbl{DejaVuSans-Bold} \newcommand{\danger}{\smbl ⚠︎\normalfont} \newcommand{\warnformat}{\sffamily\bfseries \color{red}} \newcommand{\textwarn}{This document is {\underline{NOT}} redacted. It contains private and confidential personal data, and may {\underline{NOT}} be distributed, published, or shown to those without the right to view such information. The publication of the information in this document may constitute a contempt of court, punishable by a term of imprisonment.} \newcommand{\textsafe}{This document has been altered to remove sensitive personal data. It is cleared for publication and dissemination. } \definecolor{darkgreen}{rgb}{0.0, 0.2, 0.13} \definecolor{darkspringgreen}{rgb}{0.09, 0.45, 0.27} \definecolor{forestgreen}{rgb}{0.13, 0.55, 0.13} \newcommand{\dquad}{\danger\danger\danger\danger} \newcommand{\dangersign}[1]{\scalebox{2}{\huge\danger}} \newcommand{\dangerblock}{\scalebox{2}{\huge\danger\quad\danger\quad\danger}} \newcommand{\warnblock}{{\Large\wrnstncl\warnword\quad\warnword\quad\warnword}} \newcommand{\tworules}{\hrule width \hsize height .7pt\vskip2pt\hrule width \hsize height .7pt} \newcommand{\allwarning}{\dangerblock\\\warnblock\\\normalfont\smallskip\warnformat\textwarn } \newcommand{\confwarning}{% %%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%% % The warning option %%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%% \ifwarning \ifcnsr %%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%% % a note saying document is redacted %%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%% \begin{center} \color{forestgreen} \tworules\vskip5pt \normalsize\normalfont\sffamily\bfseries\textsafe \vskip5pt\tworules \end{center} \else %%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%% % The WARNING for un-redacted docs %%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%% \begin{center}\color{red}\tworules\vskip 5pt\allwarning \vskip5pt\tworules% \end{center}% \fi% \else\fi} %%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%% % Allow \maketitle % on same page % yay % %%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%% \ifwarning\let\oldmaketitle\maketitle\renewcommand{\maketitle}{{\let\newpage\relax\maketitle}}\else\fi %%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%% % print the warning at the start of the document %%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%% \AtBeginDocument{\confwarning}