使用 pandoc 和 xelatex 从 markdown 生成 pdf 会产生误导性错误消息

使用 pandoc 和 xelatex 从 markdown 生成 pdf 会产生误导性错误消息

当我尝试转换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 字节序列。举个例子,在字符串中应该把,字符为:

因此,整个字符串以 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 8A2e 2e 2e...

相关内容