5

I want my minted listings not to have copyable text. I've found this:

Is it possible to produce a PDF with un-copyable text?

But after a few hours of trials I am not able to integrate it with my document. I am using a simple LaTeX report document and am able to build it with either XeTeX or LuaLaTeX.

Example document:

% lualatex -shell-escape minted.tex

\begin{filecontents*}{test.rb}
# this should not be copyable
def hello
  "world"
end
\end{filecontents*}

\documentclass[12pt,twoside,openright,a4paper]{report}
\usepackage[T1]{fontenc}
\usepackage{fontspec}
\usepackage[dvipsnames]{xcolor}
\usepackage{minted}
\setmainfont{Times New Roman}
\definecolor{bg}{rgb}{0.95,0.95,0.95}
\begin{document}
\chapter{Foo}
\section{Bar}
This should be copyable.
\inputminted[linenos, numbersep=5pt, tabsize=2, bgcolor=bg]{ruby}{test.rb}
And this again should be copyable.
\end{document}

Document

How I would like the copied text to look like:

Chapter 1 Foo
1.1 Bar
This should be copyable.

And this again should be copyable.
7
  • Hi! Share an example of your code with us. Have a look about minimal working example. – Keks Dose Nov 27 '19 at 11:55
  • Thanks @KeksDose. Added minimal example. – Ernesto Nov 27 '19 at 16:33
  • That's pretty useless: just do a screen capture and pass it to an online OCR service. – egreg Dec 2 '19 at 11:03
  • Obviously a part of PDF is no copyable if that part it's an image. This can be an idea. – vi pa Dec 2 '19 at 12:48
  • 1
    I know and don't care about OCR services. This is just a hard requirement at my faculty. Discussing it is pointless. Generating images is OK as long as this can be automated. But I fear that it could be difficult since these listings can cover multiple pages. – Ernesto Dec 2 '19 at 23:38
7
+50

First, I fully agree with Jesse Knight:

  • Whoever made this requirement at your faculty is a sadist

But if you use LuaTeX you can destroy the glyph to unicode mapping in the PDF file by creating a special font feature:

% lualatex -shell-escape minted.tex

\begin{filecontents*}{test.rb}
# this should not be copyable
def hello
  "world"
end
\end{filecontents*}

\documentclass[12pt,twoside,openright,a4paper]{report}
\usepackage[T1]{fontenc}
\usepackage{fontspec}
\usepackage[dvipsnames]{xcolor}
\usepackage{minted}
\setmainfont{Times New Roman}
\usepackage{luacode}
\begin{luacode*}
  local function nocopy(tfmdata)
    for _, c in next, tfmdata.characters do
      c.tounicode = nil
    end
  end
  fonts.handlers.otf.features.register {
    name        = "nocopy",
    description = "disallow copying for this font",
    manipulators = {
        base = nocopy,
        node = nocopy,
        plug = nocopy,
    }
  }
\end{luacode*}
\setmonofont[RawFeature=nocopy]{Latin Modern Mono}
\definecolor{bg}{rgb}{0.95,0.95,0.95}
\begin{document}
\chapter{Foo}
\section{Bar}
This should be copyable.
\inputminted[linenos, numbersep=5pt, tabsize=2, bgcolor=bg]{ruby}{test.rb}
And this again should be copyable.
\end{document}

With this approach you can not use the same font in the copyable and the not copyable part, so if you load any font once with nocopy and once without, only the first setting will be respected.

If you do not want to affect "regular" \texttt text and verbatim material, you can select a special font for minted blocks:

% lualatex -shell-escape minted.tex

\begin{filecontents*}{test.rb}
# this should not be copyable
def hello
  "world"
end
\end{filecontents*}

\documentclass[12pt,twoside,openright,a4paper]{report}
\usepackage[T1]{fontenc}
\usepackage{fontspec}
\usepackage[dvipsnames]{xcolor}
\usepackage{minted}
\setmainfont{Times New Roman}
\usepackage{luacode}
\begin{luacode*}
  local function nocopy(tfmdata)
    for _, c in next, tfmdata.characters do
      c.tounicode = nil
    end
  end
  fonts.handlers.otf.features.register {
    name        = "nocopy",
    description = "disallow copying for this font",
    manipulators = {
        base = nocopy,
        node = nocopy,
        plug = nocopy,
    }
  }
\end{luacode*}
\newfontfamily\nocopyfont[RawFeature=nocopy]{Courier New}
\renewcommand\myFont{\nocopyfont}
\fvset{fontfamily=myFont}
\definecolor{bg}{rgb}{0.95,0.95,0.95}
\begin{document}
\chapter{Foo}
\section{Bar}
This should be copyable.
\inputminted[linenos, numbersep=5pt, tabsize=2, bgcolor=bg]{ruby}{test.rb}
And this again should be copyable (\texttt{This too}).
{
\tracingall\nocopyfont
}
\end{document}
8
  • Thanks, I liked your answer. Where can I start if I were to try my hands on lualtex font API? – reportaman Dec 5 '19 at 5:49
  • This looks great! But can I somehow make regular \texttt{text} copyable when using this? I can use a different font for minted listing if that would be a concern. – Ernesto Dec 5 '19 at 12:18
  • Yes, you will have to use a different font, then that will work. – reportaman Dec 5 '19 at 22:57
  • @Ernesto I added an example which uses another font to archive that. – Marcel Krüger Dec 5 '19 at 23:01
  • @reportaman You mean like documentation? Basically the structure of the font table (what is stored in tfmdata here) can be found in the LuaTeX manual (they also explain tounicode etc.) Beside that, there are some parts of ConTeX's font API, especially the fonts table, available from LuaLaTeX. Some info about that should be in onandon.pdf and fonts-mkiv.pdf. – Marcel Krüger Dec 5 '19 at 23:13
3

Solution

Perhaps my biggest hack yet ...

main.tex: compile with --shell-escape to allow \write18 commend

\documentclass[12pt,twoside,openright,a4paper]{report}
\usepackage{graphicx}
\newcommand{\nocopycode}[2]{%
  \immediate\write18{./code.sh #1 #2}
  \\[\textfloatsep]
  \includegraphics[width=\linewidth]{{#2}.png}%
  \\[\textfloatsep]
}
\usepackage{listings}
\begin{document}
\chapter{Foo}
\section{Bar}
This should be copyable.
\nocopycode{ruby}{hello.rb}
And this again should be copyable.
\end{document}

code.sh: you may have to chmod +x code.sh to allow the script to be executed

echo \\def\\xstyle{$1} \\def\\xfile{$2} > code-config.tex
pdflatex -interaction=nonstopmode code.tex
convert -trim -density 300 code.pdf $2.png

code.tex: where convert is imagemagick

\documentclass{article}
\usepackage{listings}
\usepackage{xcolor}
\lstset{
  basicstyle=\ttfamily,
  backgroundcolor=\color{lightgray},
}
\input{code-config.tex}
\begin{document}
  \pagenumbering{gobble}
  \lstinputlisting[language={\xstyle}]{\xfile}
\end{document}

... producing main.pdf:

output

Notes:

  • Feel free to play around with code.tex to get the styling you like
  • You may find you have aliasing artifacts in the code images, not much we can do
  • If you have a lot of code, your document size is going to be large
  • Don't ask me to get this working on Windows
  • Whoever made this requirement at your faculty is a sadist
2
  • Thanks for your answer. The other solution is better as it doesn't require immediate steps, but this is a cool feature of the TeX engine that I had no idea about and might be handy in the future. :) – Ernesto Dec 5 '19 at 21:50
  • No problem. I agree, and I upvoted the other answer :) Just be careful with --shell-escape (i.e. don't use this by default) as it makes you vulnerable, e.g. if you ever download and compile a random package / file from online, it could potentially execute arbitrary shell code. – jessexknight Dec 5 '19 at 22:16
3

If I were in your situation, I would follow these 3 steps:

  1. Export typeset minted listing to a pdf (standalone if it fits on same page or use another documentclass if listing spans multiple pages),
  2. Run ghostscript to convert text to vector paths,
  3. Import the vector path pdf produced by ghostscript in position of minted environment.

Here's an example (with standalone):

  1. Step-1: Lets say we have every minted code in a standalone file (there might be other ways to export inline code to a separate pdf, I don't know how)
% minted.tex
\documentclass{standalone}
\usepackage{minted}
\begin{document}
\begin{minipage}[t]{0.5\textwidth}
\begin{minted}{bash}
echo "Hello, world!"
\end{minted}
\end{minipage}
\end{document}

Run your favorite latex engine, I used >>lualatex --shell-escape minted.tex. It will produce minted.pdf that looks like: copyable text

  1. Run following script using command: >>./fonttopath.sh minted.pdf mintedpath.pdf
% fonttopath.sh
#!/bin/sh
if [ "x$1" = "x" -o "x$2" = "x" ]; then
        echo Usage: `basename "$0"` "<input.pdf>" "<output.pdf>" >&2
        exit 1
fi
gs -o "$2" -sDEVICE=pdfwrite -dNOCACHE -dNoOutputFonts -dNOSUBSTDEVICECOLORS -c 60000000 setvmthreshold -f "$1"
  1. Import mintedpath.pdf in place where you were planning to have your minted code.*

Advantage of this solution: 1. nothing will be rasterized, 2. file size will be smaller compared to exporting & importing image formats like jpeg, etc, 3. print quality won't degrade, 4. As requested in your comments, this also works for long code blocks that span multiple pages, for that use documentclass like article instead of standalone, and set geometry of page to match that of your report. Ghostscript can convert a pdf spanning pages to an equivalent vector path pdf spanning multiple pages that can then be imported as if you were adding another pdf document in your latex code.

  • Note: Depending on the pdf viewer you might still be able to select these vector paths, but try pasting them into a word processor or editor to realize that it doesn't copy any text (because no text is left at this point).
1
  • This combined with the \write18 solution would be neat if the LuaLaTeX didn't allow to destroy Unicode info. But thanks :) – Ernesto Dec 5 '19 at 21:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.