5

I'm typesetting litterate programs; they look like this:

\documentclass{article}

\usepackage{fontspec}
\newfontfamily{\Symbola}{Symbola}
\newfontfamily{\UbuntuMono}{Ubuntu Mono}
\DeclareTextFontCommand{\S}{\Symbola}

\usepackage{xcolor}

\newenvironment{code}{\setlength{\parindent}{0pt}\UbuntuMono}{}

\begin{document}
\begin{code}
  (fold\_left (\S{λ} x \textcolor{red}{y}.  x \S{⊕} \textcolor{red}{y})\\
             (a \S{∷} b \S{∷} c \S{∷} d \S{∷} nil))
\end{code}
\end{document}

How can I make sure that the code environment perserves all whitespace? In particular, that whitespace should be in the generated PDF, too: that is, copy-pasting from the PDF should copy the whitespace as well.

Here are a few attempts:

  • Use \hspace{}: not very nice when looking at the sources, and spaces can't be copied from resulting PDF.
  • Replace all spaces by ~, and add \- for beginning-of-line spaces. Sources become ugly (not a show-stopper), but spaces still can't be copied.
  • Use \catcode32=12 in definition of code. This works fine alignment-wise, it looks perfect… but spaces still can't be copied.
  • Using the listings package: would be great, but it dies on Unicode, and I'm tired of the litterate-based workarounds
  • Using the fancyvrb package: Has an escapechar property that could be promising, and line breaks being significant in the source are not a deal-breaker, but it seems heavyweight (?). Ideally, I'd like to be able to use this environment inline, too.

What magic do I need to make my environment respect all spaces, and make them copy-pastable from the PDF?

  • have you tried minted (example Q here -- might be relevant as it's XeTeX). I'm not an expert as I use pdflatex+listings for exactly what you want minus the unicode but I've heard good thigns about it. – Chris H Aug 3 '16 at 8:27
  • 1
    @ChrisH: Minted definitely doesn't work for me; I can't rely on a Python installation being available, and I don't need any automatic syntax highlighting. – Clément Aug 3 '16 at 11:22
  • Producing white space in the document might be doable (i.e., \newenvironment{code}{\obeyspaces\setlength{\parindent}{0pt}\UbuntuMono}{} and then using \\\mbox{} instead of \\ ), but the copy/paste in the PDF is much more problematic, I think. If I recall, PDF doesn't use spaces in the traditional sense, but individually moves the "cursor" before placing each word. The "moving the cursor" part is not made equivalent to some number of blank spaces. – Steven B. Segletes Aug 3 '16 at 14:23
  • @StevenB.Segletes Thanks for the pointer to obeyspaces! – Clément Aug 3 '16 at 16:41
  • Regarding whitespaces: I'm sure it can be done: iI print this page to PDF in chromium, copy pasting the "code" part of my post respects spaces, and I can select them individually. – Clément Aug 3 '16 at 16:42
4

ANSWER REVISITED

When it was pointed out to me that, except in computer modern, my original answer was no different that what the OP was able to achieve with \catcode32=12, I took another look at things. I came up with a wholly satisfactory answer when using pdflatex, and a less satisfying one for xelatex. In both cases, they start by making the space an active character and then redefining it to print a particular character in white.

EDITED to make the space the proper width.

-----pdflatex

Here I use package palatino just to make sure I am not relying on some special aspect of computer modern. Here, the "particular character" I print for a space, in white, is the visible space from the lmodern font.

\documentclass{article}
\usepackage{xcolor}
\usepackage{palatino}
\newsavebox\spacewd
\savebox\spacewd{\texttt{ }}
\newenvironment{code}{\par\catcode32=\active \setlength{\parindent}{0pt}\ttfamily}{\par}
{
\catcode32=\active %
\gdef {\makebox[\wd\spacewd][l]{%
\textcolor{white}{\fontfamily{lmtt}\selectfont\large\smash{\char32}}}}% 
}
\begin{document}
\noindent xyz
\begin{code}
  (fold\_left (\S{λ} x \textcolor{red}{y}.  x \S{⊕} \textcolor{red}{y})\\
             (a \S{∷} b \S{∷} c \S{∷} d \S{∷} nil))
\end{code}
\noindent this is
  a
    test to make    sure all 
      is back to normal
\end{document}

enter image description here

The PDF copy/paste performs as follows:

enter image description here

which is exactly what is desired.

-----xelatex

Here I use libertine font. The only quirk here is that \textvisiblespace takes up less horizontal width than a \ttfamily space and so to avoid the PDF from interpreting the spaced out \textvisiblespaces as a double space, I made the \textvisiblespace that gets inserted both \large and \smashed to avoid PDF interpreting it as an extra gap. Further, I make the \makebox left aligned, so that the large white \textvisiblespace didn't overwrite the prior character.

\documentclass{article}
\usepackage{lmodern}
\usepackage{xcolor}
\usepackage{fontspec,libertine}
\newsavebox\spacewd
\savebox\spacewd{\texttt{ }}
\newenvironment{code}{\par\catcode32=\active \setlength{\parindent}{0pt}\ttfamily}{\par}
{
\catcode32=\active %
\gdef {\makebox[\wd\spacewd][l]{\textcolor{white}{\large\smash{\textvisiblespace}}}}% 
}

\begin{document}
\noindent xyz
\begin{code}
  (fold\_left (\S{λ} x \textcolor{red}{y}.  x \S{⊕} \textcolor{red}{y})\\
             (a \S{∷} b \S{∷} c \S{∷} d \S{∷} nil))
\end{code}
\noindent this is
  a
    test to make    sure all 
      is back to normal
\end{document}

enter image description here

The resulting copy/paste from the PDF is

enter image description here

in which another character is substituted for the space, in this case I had designated \textvisiblespace. I verified that my TeXworks editor can replace the invisible space character with a real space on a global basis.

ORIGINAL ANSWER

This answer is based on my year-old suggestion of using \obeyspaces in the code environment to preserve blank spaces. However, as noted in the comments, it does not detect leading spaces on a line.

To remedy that, I used egreg's answer at Using end-of-line delimiter in plain Tex macro. With it, I am able to intercept the end of line (EOL) inside the code environment. I use this fact to place a \mbox{} at the beginning of each new line inside code, so that the \obeyspaces can detect the leading blank spaces of each subsequent line.

The hard part (for me) was restoring the EOL to its rightful place upon exiting code. I did that with a test on a flag macro \endthis, which gets changed from F to T during the exit code from code.

However, the underlying problem of copy/paste of whitespace from the PDF remains.

\documentclass{article}

%\usepackage{fontspec}
%\newfontfamily{\Symbola}{Symbola}
%\newfontfamily{\UbuntuMono}{Ubuntu Mono}
%\DeclareTextFontCommand{\S}{\Symbola}

\usepackage{xcolor}

\newenvironment{code}
{\par\setlength{\parindent}{0pt}\obeyspaces\ttfamily\gdef\endthis{F}\mymacro}
{\gdef\endthis{T}\par}

\def\mymacro{%
  \if\endthis T\def\next{}\else\def\next{\begingroup\catcode`\^^M=12 \xmymacro}\fi\next}
{\catcode`\^^M=12 %
 \gdef\xmymacro#1^^M{\mbox{}#1\endgroup\mymacro}%
}
\begin{document}
\noindent xyz
\begin{code}
  (fold\_left (\S{λ} x \textcolor{red}{y}.  x \S{⊕} \textcolor{red}{y})\\
             (a \S{∷} b \S{∷} c \S{∷} d \S{∷} nil))
\end{code}
\noindent this is
  a
    test to make    sure all 
      is back to normal
\end{document}

enter image description here

The use of \catcode32=12, which the OP had mentioned, in the definition of code is similar, but it produces "visual spaces"

enter image description here

  • If you don't need to solve the issue of spaces not being copyable from the PDF, isn't \catcode32=12 a much simpler solution? The OP wrote “Use \catcode32=12 in definition of code. This works fine alignment-wise, it looks perfect… but spaces still can't be copied.” How is this better? Can you compare the merits of this solution and that one? – ShreevatsaR Aug 24 '17 at 15:23
  • @ShreevatsaR See addendum to my answer. – Steven B. Segletes Aug 24 '17 at 15:31
  • 1
    The visible spaces are an idiosyncrasy of Computer Modern; the problem isn't present with the OP's chosen font (Ubuntu Mono). – ShreevatsaR Aug 24 '17 at 15:37
  • @ShreevatsaR In that case, it would appear my solution is a sophisticated redundancy. – Steven B. Segletes Aug 24 '17 at 16:31
  • :-) It's instructive nevertheless. Too bad the PDF-copy problem (which keeps coming up) doesn't have a great solution. – ShreevatsaR Aug 24 '17 at 17:21

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.