我正在排版文学程序;它们看起来像这样:
\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}
我如何确保code
环境保留所有空格?具体来说,空格也应存在于生成的 PDF 中:也就是说,从 PDF 复制粘贴也应复制空格。
以下是一些尝试:
- 使用
\hspace{}
:查看源时不是很好,并且无法从生成的 PDF 中复制空格。 - 将所有空格替换为
~
,并\-
在行首添加空格。源代码变得丑陋(不是令人讨厌的),但空格仍然无法复制。 \catcode32=12
在 的定义中使用code
。从对齐角度来看,这可以很好地工作,看起来很完美……但空格仍然无法复制。- 使用
listings
包:会很棒,但它在 Unicode 上失效了,而且我厌倦了literate
基于 - 的解决方法 - 使用
fancyvrb
包:具有一个escapechar
可能有前途的属性,并且源代码中重要的换行符并不是一个交易破坏者,但它似乎很重(?)。理想情况下,我也希望能够内联使用此环境。
我需要什么魔法才能让我的环境尊重所有空间,并使它们可以从 PDF 中复制粘贴?
答案1
重新审视答案
当有人向我指出,除了现代计算机之外,我最初的答案与 OP 能够实现的答案没有什么不同时 \catcode32=12
,我又重新审视了一下。当使用 pdflatex 时,我得到了一个完全令人满意的答案,而对于 xelatex,我得到了一个不太令人满意的答案。在这两种情况下,他们首先将空格设为活动字符,然后重新定义为用白色打印特定字符。
已编辑以使空间具有适当的宽度。
-----pdflatex
这里我使用包palatino
只是为了确保我不依赖现代计算机的某些特殊方面。这里,我用白色打印的空格“特定字符”是字体中可见的空格lmodern
。
\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}
PDF 复制/粘贴操作如下:
这正是我们想要的。
-----赛莱特
这里我使用了libertine
字体。这里唯一的怪癖是\textvisiblespace
占用的水平宽度比\ttfamily
空格少,因此为了避免 PDF 将间隔开的\textvisiblespace
s 解释为双空格,我制作了\textvisiblespace
插入的\large
和\smash
ed 以避免 PDF 将其解释为额外的间隙。此外,我使 左\makebox
对齐,这样大白色\textvisiblespace
就不会覆盖前一个字符。
\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}
从 PDF 复制/粘贴的结果为
其中另一个字符代替空格,在本例中我指定了\textvisiblespace
。我验证了我的 TeXworks 编辑器可以全局用真实空格替换不可见的空格字符。
原始答案
这个答案是基于我一年前\obeyspaces
在code
环境中使用以保留空格的建议。但是,正如评论中指出的那样,它不会检测行上的前导空格。
为了解决这个问题,我使用了 egreg 的答案在纯 Tex 宏中使用行尾分隔符。有了它,我能够在环境中截取行尾 (EOL) code
。我利用这个事实在 中将 放在\mbox{}
每个新行的开头code
,以便\obeyspaces
可以检测到每个后续行的前导空格。
对我来说,最难的部分是在退出时将 EOL 恢复到其正确位置code
。我通过对标志宏进行测试来完成此操作,该标志宏在退出代码期间\endthis
从 更改F
为。T
code
但是,从 PDF 复制/粘贴空白的根本问题仍然存在。
\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}
\catcode32=12
OP 提到的 的定义中的 的使用code
是类似的,但它产生了“视觉空间”