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.
Ernesto
  • 53

3 Answers3

7

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}
  • Thanks, I liked your answer. Where can I start if I were to try my hands on lualtex font API? – codepoet Dec 05 '19 at 05: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 05 '19 at 12:18
  • Yes, you will have to use a different font, then that will work. – codepoet Dec 05 '19 at 22:57
  • @Ernesto I added an example which uses another font to archive that. – Marcel Krüger Dec 05 '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 05 '19 at 23:13
  • @MarcelKrüger Thanks, yes that's what I ask. I have been wanting to learn lua & luatex to be able to manipulate fonts. Am new to all three: lua, luatex, and font formats, though do have have few years of coding experience, so understand basics of programming. Seeing comments in your code will help too :) – codepoet Dec 06 '19 at 01:33
  • @MarcelKrüger thanks. You are a certified TeX wizard! :) – Ernesto Dec 06 '19 at 13:14
  • @MarcelKrüger extra question... can line numbers be excluded from copying as well? – Ernesto Dec 09 '19 at 14:39
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
jessexknight
  • 2,732
  • 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 05 '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 05 '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).
codepoet
  • 1,316
  • This combined with the \write18 solution would be neat if the LuaLaTeX didn't allow to destroy Unicode info. But thanks :) – Ernesto Dec 05 '19 at 21:51