![如何保留从 LaTeX PDF 复制的 Python 代码中的缩进?](https://linux22.com/image/371152/%E5%A6%82%E4%BD%95%E4%BF%9D%E7%95%99%E4%BB%8E%20LaTeX%20PDF%20%E5%A4%8D%E5%88%B6%E7%9A%84%20Python%20%E4%BB%A3%E7%A0%81%E4%B8%AD%E7%9A%84%E7%BC%A9%E8%BF%9B%EF%BC%9F.png)
我尝试将 Python 3.6 代码包含在 LaTeX PDF 文档中,该文档应该可以轻松复制以保存到文件或尝试 Python 代码。
虽然,
\begin{verbatim}
for row in range(1,9):
for col in range(1,9):
print(int(str(row)+str(col)))
\end{verbatim}
在 PDF 中显示得很好,但如果我将其复制并粘贴到文本编辑器中,它看起来像:
for row in range(1,9):
for col in range(1,9):
print(int(str(row)+str(col)))
Python 必需的缩进已经消失。
我也尝试使用 listings 包这里的建议:
如何在 LaTeX Listings \lstinputlistings 命令中突出显示 Python 语法
我可以简单地给出 Python 文件的文件名,例如
\pythonexternal{Test.py}
源代码将被包含并着色。但复制粘贴时开头缺少空格的情况也一样。
如果我使用选项“showspaces=true”,我会得到以下结果:
for␣row␣in␣range(1,9):
␣␣␣␣for␣col␣in␣range(1,9):
␣␣␣␣␣␣␣␣print(int(str(row)+str(col)))
这也不适合复制粘贴。好吧,我可以用空格替换所有 ␣...这不是一个真正可行的解决方案。
这里描述了 2011 年发生的一些奇怪的黑客事件:
从那时起有什么新东西吗……?有什么想法可以在 LaTeX PDF 中实现 Python 代码的复制和粘贴吗?感谢您的建议。
答案1
还有一个解决方案在 Acrobat Reader 和 Chrome 中都可以很好地运行,但仅与 pdfTeX 兼容(不与 XeTeX/LuaTeX 兼容):
\usepackage{transparent}
\makeatletter
\def\@xobeysp{\leavevmode\nobreak\texttransparent{0}{\char32}}
\makeatother
用于\texttransparent
使 ⎵ 符号不可见(实际上,在某些字体中,插槽 32 被映射到真实空格字符上,并且这个技巧不是必需的,但上述代码涵盖了所有情况)。
最有可能的是,此代码之所以有效,是因为生成的文件不是基于 Unicode 的,并且 PDF 查看器没有关于代码 32 与实际 Unicode 字符之间匹配的信息,因此它将其“按原样”复制为字符代码 32(即空格字符)。支持这一假设的是,有失败的 XeTeX/LuaTeX 生成的文件和通过/ActualText
命令设置 Unicode 对应的尝试(通过替换\char32
上述\pdfliteral direct{/Span<</ActualText<FEFF0020>>> BDC}\char32\pdfliteral direct{EMC}
代码中的 )。
答案2
pdfTeX 有命令\pdffakespace
在文档中插入空格。这个空格是不可见的,但在文本复制过程中会被考虑在内。
因此,我的第一个想法是在每个行首的空格后插入\pdffakespace
,但不幸的是,它导致复制的文本中的空格加倍(一个空格来自\pdffakespace
,另一个空格由 Acrobat Reader 自动从 TeX 生成\hskip
)。
下一个想法是计算行首空格的数量,并\pdffakespace
在这些空格后插入所需数量的空格。不幸的是,Acrobat Reader 将这些空格修剪为一个空格(尽管空格命令序列[( )]TJ
实际上显示在生成的 PDF 文件中)。
但最终,我找到了一个棘手的解决方案:将空格分组,并用双倍空格宽度的序列替换它们\pdffakespace
。\hskip
从标准序言开始,制作@
一封信
\makeatletter
我们通过在命令定义末尾\@verbatim
添加来重新定义命令(以处理换行后的文本):\hook@par
\par
\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}%
}
然后我们创建一个计数器来计算行首的空格数
\newcount\nspaces
在行首重置此计数器,并计算空格的序列
\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}
最后,我们打印成对的空格(对奇数个空格有特殊处理,在这种情况下,我们最终会\pdffakespace
在单个空格之后输出一个空格\hskip
,该空格将与前一个空格合并\hskip
,并在文本复制期间产生单个空格)
\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}
最后别忘了恢复@
回来:
\makeatother
就这样。 瞧。
答案3
另一种方法是使用hyperref
包并创建多行文本字段:
\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}
在这种情况下,您也可以从 Chrome 复制文本,但似乎没有办法调整文本字段的文本字体(它始终是默认的无衬线字体)。而且它在 SumatraPDF 中不起作用(尽管在 SumatraPDF 中复制文本效果很差,即使是普通文本)。
PS. 这只是一个概念验证(因为我猜没人会输入所有这些\space
以及\string\n
真实的代码片段)。