10

I tried to include Python 3.6 code in a LaTeX PDF document which should be easily be copied to save to a file or to try the Python code.

Although,

\begin{verbatim}
for row in range(1,9):
    for col in range(1,9):
        print(int(str(row)+str(col)))
\end{verbatim}

shows nicely in the PDF but if I copy it and paste it to a text editor it looks like:

for row in range(1,9):
for col in range(1,9):
print(int(str(row)+str(col)))

The indentations which are essential for Python are gone.

I also tried the suggestion here using the listings package:

How to highlight Python syntax in LaTeX Listings \lstinputlistings command

where I can simply give the file name of an Python file e.g.

\pythonexternal{Test.py}

And the source code will be included and colored. But the same thing with the missing spaces at the start when copy&pasting.

If I use the option "showspaces=true" I get the following:

for␣row␣in␣range(1,9):
␣␣␣␣for␣col␣in␣range(1,9):
␣␣␣␣␣␣␣␣print(int(str(row)+str(col)))

which is also not suitable for copy&paste. Well, I could replace all ␣ with space... not a really practicable solution.

There have been some weird hacks described in 2011 here:

How to make listings code indentation remain unchanged when copied from PDF?

Is there anything new since then...? Any ideas how to achieve copy&paste Python code in a LaTeX PDF? Thank you for suggestions.

7
  • 1
    I used once attachfile package to get code snippets attached to the PDF (but not every PDF viewer will display them). I also used filecontentsdef to create those snippet files from LaTeX source, but you might prefer VerbatimOut or like environments.
    – user4686
    Feb 25, 2018 at 21:22
  • I prefer not to use something which is displayed (or not) depending on the viewer. I couldn't try filecontentsdef. How to include? \usepackage{filecontensdef}? It should be included in MikTeX but TeXnicCenter didn't load it. VerbatimInput would be an option, but when copying the Python code from PDF to text editor the indentations are also gone. As I understand the above mentioned thread and hacks from 2011, there was no working solution.
    – theozh
    Feb 26, 2018 at 7:45
  • you did not understand. I referred to VerbatimOut from package fancyvrb which allows (like filecontentsdef) to create some file from within the LaTeX source. Then you attach these files with package attachfile. As per filecontentsdef you need to know how to update your TeX installation this is not the problem here.
    – user4686
    Feb 26, 2018 at 12:08
  • maybe, I did not make myself clear. I don't want to create an external file from LaTeX code and I don't want to attach a file. I want to include source code from an external file which can be selected and copied later from the resulting PDF and pasted into a text editor including indentations.
    – theozh
    Feb 26, 2018 at 19:32
  • I know no way to copy paste from PDF (especially if you don't want to restrict to specific viewers) and preserve code indentation. I proposed file attachments as the only foolproof method I know of. HTML or for that matter plain text are simply better than PDF. This is major issue with PDF.
    – user4686
    Feb 26, 2018 at 22:35

3 Answers 3

2
+100

One more solution that works well in both Acrobat Reader and Chrome, but is compatible with pdfTeX only (not XeTeX/LuaTeX):

\usepackage{transparent}
\makeatletter
\def\@xobeysp{\leavevmode\nobreak\texttransparent{0}{\char32}}
\makeatother

The \texttransparent is used to make ⎵ symbol invisible (actually, in some fonts the slot 32 is mapped onto the real space character, and this trick is not necessary, but the above code covers all cases).

Most likely, this code works because the generated file is not Unicode-based, and PDF viewer has no information about matching between code 32 and actual Unicode character, and so it copies it "as-is" as character code 32 (i.e. the space character). In favor of this hypothesis, there are failed XeTeX/LuaTeX-generated files and an attempt to set Unicode correspondence via /ActualText command (by replacing \char32 with \pdfliteral direct{/Span<</ActualText<FEFF0020>>> BDC}\char32\pdfliteral direct{EMC} in the above code).

4
  • Thanks. As far as I can tell, your solution seems to work with Acrobat. By the way, here is the other solution of @StephanLemke which I mentioned in the comments to my question. See tex.stackexchange.com/a/148661/155803 Yours is even shorter, but I have not tested other viewers with your solution. However, it looks like "nothing" seems to work with SumatraPDF.
    – theozh
    Jan 12, 2021 at 9:11
  • The problem with this and your other suggestions is that they don't work reliably across the pdf viewers. Adobe honors /Actualtext but drops spaces, Sumatra ignores /Actualtext (and copies you invisible char as U+2423) and so on. Jan 12, 2021 at 11:06
  • @UlrikeFischer ok, so does this mean either 1. every viewer needs its special solution or 2. there can't be one solution which will work for "all" PDF viewers or 3. some viewers won't allow a working solution?
    – theozh
    Jan 12, 2021 at 12:24
  • @DenisRyabov since I'm using PDFLaTeX and not XeLaTeX or LuaLaTeX, it looks like this (shortest) of your suggestions works for me in the verbatim environment and AcrobatReader. For other code or code from an external file the solution described here tex.stackexchange.com/a/19978/155803 seems to work as well using the listings package.
    – theozh
    Jan 15, 2021 at 9:29
2

The pdfTeX has command \pdffakespace to insert a space into the document. This space is invisible, but it is taken into account during text copying.

So, my first idea was to insert \pdffakespace after each space at the beginning of lines, but unfortunately, it results in spaces doubling in the copied text (one space comes from \pdffakespace and another is generated by Acrobat Reader automatically from TeX's \hskip).

Next idea was to count the number of leading spaces in the line and insert the necessary number of \pdffakespace after those spaces. Unfortunately, Acrobat Reader trims that spaces to a single one (in spite of the fact that the sequence of space commands [( )]TJ is actually presented in the generated PDF file).

But finally, I've found a tricky solution: group spaces into pairs and replace them with a sequence of \pdffakespace and \hskip of double space width. Starting with a standard preamble to make @ a letter

\makeatletter

we redefine the \@verbatim command by adding \hook@par at the end of \par command definition (to process text after line break):

\def\@verbatim{\trivlist \item\relax
  \if@minipage\else\vskip\parskip\fi
  \leftskip\@totalleftmargin\rightskip\z@skip
  \parindent\z@\parfillskip\@flushglue\parskip\z@skip
  \@@par
  \language\l@nohyphenation
  \@tempswafalse
  \def\par{%
    \if@tempswa
      \leavevmode \null \@@par\penalty\interlinepenalty
    \else
      \@tempswatrue
      \ifhmode\@@par\penalty\interlinepenalty\fi
    \fi\hook@par}% <=== HERE
  \let\do\@makeother \dospecials
  \obeylines \verbatim@font \@noligs
  \everypar \expandafter{\the\everypar \unpenalty}%
}

Then we create a counter to count spaces at the beginning of lines

\newcount\nspaces

reset this counter at the beginning of lines, and count the sequence of spaces

\def\hook@par{\nspaces=0\relax\check@space}
\def\check@space{\futurelet\@let@token\do@check@space}
\def\do@check@space{%
  \ifx\@let@token\@xobeysp%
    \advance\nspaces 1%
    \expandafter\skip@space%
  \else%
    \ifnum\nspaces>0%
      \print@spaces%
    \fi%
  \fi}
\def\skip@space#1{\check@space}

And finally, we print paired spaces (with a special treat for the odd number of spaces, in this case, we finally out \pdffakespace following a single-space \hskip that will be merged with the previous \hskip and results in a single space during text copying)

\def\print@spaces{%
  \leavevmode\nobreak
  \loop%
    \pdffakespace%
    \nobreak\hskip\dimexpr 2\fontdimen2\font\relax%
    \advance\nspaces by -2\relax%
  \unless\ifnum\nspaces<2\repeat%
  \ifnum\nspaces>0%
    \nobreak\hskip\fontdimen2\font\relax%
    \pdffakespace%
  \fi}

In the end, don't forget to restore @ back:

\makeatother

That's all. Voilà.

1
  • Thank you for your suggestion. Copying with indentations seems to work in AcrobatReader, but not in SumatraPDF and not in ChromeBrowser (Windows). Haven't yet had the chance to test other viewers or operating systems. What I've read so far is that this seems to be highly viewer dependent, so probably there won't be a solution which works for all.
    – theozh
    Jan 8, 2021 at 12:16
0

Alternative way is to use hyperref package and create multi-line text field:

\begin{Form}
  \TextField[multiline=true,disabled,borderwidth=0,
             width=\linewidth,height=3\baselineskip,
             value={%
  for row in range(1,9):\string\n%
  \space\space\space\space for col in range(1,9):\string\n%
  \space\space\space\space\space\space\space\space print(int(str(row)+str(col)))%
  }]{~}
\end{Form}

In this case you can copy text from Chrome too, but there is (seems to be) no way to adjust text font for text fields (it's always a default sans-serif font). And it doesn't work in SumatraPDF (though copying works badly in SumatraPDF even for usual text).

PS. This is just a proof-of-concept (as I guess nobody will type all those \space and \string\n for real code snippets).

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .