当我尝试转换markdown 文件使用 转换为 pdf pandoc
。我的 markdown 文件包含中文字符和英文字符。我使用的命令是:
pandoc --pdf-engine=xelatex -V CJKmainfont=KaiTi test.md -o test.pdf
错误信息是:
生成 PDF 时出错。
!未定义控制序列。pandoc
:无法解码字节“\xbd”:Data.Text.Internal.Encoding.streamDecodeUtf8With:无效的 UTF-8 流
事实上,这个错误与 UTF-8 编码无关。经过长时间的思考,我终于发现,这是因为我的 markdown 文件包含反斜杠后跟文本,而 pandoc 在默认设置下会将其视为 LaTeX 命令。在了解了这个关键信息后,我终于能够修复这个问题。更多信息可以在这个 pandoc 问题。
有人在该问题中建议这可能是一个问题xelatex
,因为如果我们使用
pandoc --pdf-engine=lualatex test.md -o test.pdf
错误消息变成如下内容:
生成 PDF 时出错。
!未定义控制序列。 l.416
...宽度有问题,应该把\textwidth更改为
如果使用引擎的错误消息xelatex
与上述消息类似。我早就解决了这个问题。所以在我看来,错误消息可能确实与 xelatex 有关。
但是,但是,如果我们把生成pdf的步骤分成两步,即先生成tex文件,再从tex生成pdf文件。类似下面的代码:
pandoc -s -t latex -V CJKmainfont=KaiTi test.md -o test.tex # first step xelatex test.tex # second step
然后错误信息就会改变,就像我们使用lualatex
引擎时一样。这表明问题可能与无关xelatex
。我们得到了相互矛盾的结论。
我是 pandoc 的新手,不了解其内部原理xelatex
。有谁能更了解并指出是哪个原因导致的。是 Pandoc 还是 xelatex,还是两者兼而有之?
系统和 pandoc 版本信息
我已经在 Windows 和 Linux 系统(CentOS 7)上测试了该文件。系统、pandoc、TeX Live 和 xelatex 的具体版本如下所列。
视窗
- 系统版本:Windows 8.1 32位
- Pandoc 版本:2.0.5
- TeX Live:2016/W32TeX
- xelatex:XeTeX 3.14159265-2.6-0.99996
Linux
- 系统版本:CentOS 7.2.1511
- Pandoc 版本:1.12.3.1
- TeX Live:2017
- xelatex:3.14159265-2.6-0.99998
更新 2017.12.29
随着Pandoc 2.0.6 发布,这种行为处理得更为恰当:
允许对 latex 错误日志进行宽松解码,这些日志并不总是正确的 UTF8 编码
现在,调试此类问题变得更加容易了。
答案1
XeTeX 确实可以在其错误输出中产生无效的 UTF-8,我可以使用以下更简单的.tex
文件重现此情况:
\documentclass{article}
\begin{document}
应该把 123456789 123456789 123 \textwidth换成
\end{document}
因此,您可以将其视为 XeTeX 中的错误(因为生成了无效的 UTF-8)或 Pandoc 中的错误(因为错误地假设 XeTeX 将生成有效的 UTF-8)。
Unicode 和 UTF-8
简而言之,问题在于您不能在任意位置中断 UTF-8 字节序列。举个例子,在字符串中应该把
,字符为:
- U+5E94 CJK统一表意文字-5E94,以 UTF-8 编码为
E5 BA 94
- U+8BE5 CJK统一表意文字-8BE5,以 UTF-8 编码为
E8 AF A5
- U+628A CJK统一表意文字-628A,以 UTF-8 编码为
E6 8A 8A
因此,整个字符串以 UTF-8 编码为 9 个字节的序列:
E5 BA 94 E8 AF A5 E6 8A 8A
\______/ \______/ \______/
应 该 把
您可以在 0、3、6 或 9 个字节后拆分字节序列,以分别获得包含 0、1、2 或 3 个字符的有效字符串。但在其他地方拆分会导致 UTF-8 无效。
不幸的是,这正是 XeTeX 所能做的:它可以在某个地方破坏字节序列,导致无效的 UTF-8,而 Pandoc 无法处理(因为它假定 UTF-8 有效)。
解释
首先,在 XeTeX 和 LuaTeX 等支持 Unicode 的引擎中,所有 Unicode 字符都可以成为控制序列的一部分,而恰好没有命名的控制序列,\textwidth换成
因此系统会生成有关未定义控制序列的错误。
然后,当将此错误打印到终端时,TeX 会尝试在周围添加其他上下文在哪里遇到了这个未定义的控制序列\textwidth换成
,这意味着在出现的周围会有一些额外的字符来填充error_line
字符。(这可以增加;参见这里和这里。尽管增加这个值无论如何都是个好主意,并且可以降低发生此错误的可能性;但是当行足够长时,它仍然会发生(问题中的例子中确实发生了),因为的最大值error_line
仍然只有 254。)
不幸的是(这也是一个 bug),XeTeX 似乎按字节计数并截断输出,而不考虑仅在明确定义的 Unicode 代码点序列处中断。procedure show_context
查找XeTeX 源代码并与print_valid_utf8
在 LuaTeX 源代码中,用于它是show_context
。
在这个例子中,XeTeX 只选取了第一个单词( )的最后两个字节8A 8A
,这不是有效的 UTF-8 序列。这就是 iconv 和 Pandoc 抱怨的原因。
示范
.tex
我使用LuaTeX 和 XeTeX编译上述文件所用的命令分别是:
lualatex -interaction=nonstopmode test.tex | iconv -f UTF8
和
xelatex -interaction=nonstopmode test.tex | iconv -f UTF8
使用前者(LuaTeX),我收到错误消息:
! Undefined control sequence.
l.3 ...把 123456789 123456789 123 \textwidth换成
但对于后者(XeTeX),我收到一条错误消息,指出 UTF-8 无效,因此iconv
失败
iconv: (stdin):11:7: cannot convert
如果没有iconv
,在我的终端上我看到打印:
! Undefined control sequence.
l.3 ...?? 123456789 123456789 123 \textwidth换成
通过将输出重定向到文件并在原始编辑器中查看,我们可以更好地了解发生了什么。以下是 hexdump 输出xxd -g 1 -c 32
:
000001c0: 78 29 0a 21 20 55 6e 64 65 66 69 6e 65 64 20 63 6f 6e 74 72 6f 6c 20 73 65 71 75 65 6e 63 65 2e x).! Undefined control sequence.
000001e0: 0a 6c 2e 33 20 2e 2e 2e 8a 8a 20 31 32 33 34 35 36 37 38 39 20 31 32 33 34 35 36 37 38 39 20 31 .l.3 ..... 123456789 123456789 1
00000200: 32 33 20 5c 74 65 78 74 77 69 64 74 68 e6 8d a2 e6 88 90 0a 20 20 20 20 20 20 20 20 20 20 20 20 23 \textwidth.......
注意省略号(含义)后面的( =8a 8a
的最后两个字节) 。把
E6 8A 8A
2e 2e 2e
...