50 ctex-kit下不同编译引擎以及编译编码的实验及其疑问

发布于 2025-03-16 12:36:15
(来自OsbertWang的建议) @u40

嗯……一会儿做个实验

在引入 ctex 包时,添加选项 GBK,然后将 tex文件编码改为 GBK,然后试试用 pdflatexxelatex能否编译


一些有关编码的前置知识(转译自egreg的著名回答):

  • inputenc宏包允许用户直接在键盘内直接输入重音字符(accented characters),这可以让TeX正确识别到
  • fontenc针对于输出PDF的字符结果,决定哪一种编码的字体将被用于打印字符到PDF内

从结果上看,这两个宏包是互相独立的。但建议先加载 fontenc 再加载 inputenc

宏包的核心作用

  • fontenc 的作用
    通过 \usepackage[T1]{fontenc} 选择支持欧洲主要语言(德语、法语、意大利语、波兰语等)重音字符的输出字体编码。关键意义在于,若未使用 T1 编码,TeX 无法正确对含重音字母的单词进行断字(hyphenation)
  • inputenc 的作用
    通过 \usepackage[<encoding>]{inputenc} 允许直接输入重音及其他特殊字符。<encoding> 需与文件编码一致(取决于操作系统(OS)和文本编辑器(text editor)设置)

    • 一个例子是,在使用pdflatex进行编译时,若文件使用 Latin-1(ISO 8859-1) 编码,应调用:
    \usepackage[T1]{fontenc}  
    \usepackage[latin1]{inputenc}  
    • 若仅调用 \usepackage[T1]{fontenc} 却得到正确输出,可能因文件编码恰为 Latin-1,但存在例外(如输入 ß 会错误输出 SS)。

工作流程示例(仅仅针对pdflatex引擎)

TeX开始读入文件时,其对文件编码(encoding)一无所知,TeX只通过读取到的字符码(character number)执行它的行为

对于字符"a的输入(两者正好coincide表现重合):

  • 输入阶段:在 Latin-1 编码的编辑器中,ä 存储为字符编号 228TeX 读取该字符时,inputenc 将其转换为 \"a 命令。
  • 输出阶段:fontenc\"a 映射到 T1 编码字体228 号字符(即 ä 的字形)。

对于字符ß的输入:

  • 输入阶段:识别到的字符码为223TeX机器的读入在inputenc宏的作用下,将字符码223转换为\ss
  • 输出阶段:fontenc\ss映射到255号字符,但实际上T1编码是存在ß这一字符的,这导致了一种错误的匹配

关于UTF8编码(xelatexlualatex默认均基于此,且不再需要inputenc宏包)

当使用 \usepackage[utf8]{inputenc}(且文件当然以 UTF-8 编码)时,情况略有不同。当文本编辑器显示 äß 时,文件实际包含两个字节序列(byte sequences),分别为 <C3><A4><C3><9F>

第一个字节是一个前缀(prefix),包含一些信息,主要作用是表示这是一个双字节字符(two-byte character)。此时,inputenc 会将所有合法前缀设为活跃字符(active character),因此 <C3> 的行为类似于宏(macro);它的定义是查看下一个字符,然后根据 Unicode 规则解释整个字节对,并将其转换为对应的码点(code point),即分别转换为 U+00E4U+00DF

其他前缀表示三字节或四字节组合(three or four byte combinations),但其行为本质上相同:它们不会多占用一个字符,而是吸收(absorb)两或三个后续字节,并执行到码点的转换。

ot1enc.dfut1enc.dfu 文件中,我们可以找到以下声明:

\DeclareUnicodeCharacter{00DF}{\ss}  
\DeclareUnicodeCharacter{00E4}{\"a}  

哦,等等!还有更多内容!是的,在这种情况下,inputenc 会与 fontenc 交互(而对于其他输入编码则不会):每加载一种输出编码时,对应的 .dfu 文件(Unicode 定义文件)会在文档开始前被读取。这就是为什么我倾向于始终先加载 fontenc 再加载 inputenc(尽管并非绝对必要)。这些声明提供了必要的设置:组合 <C3><A4><C3><9F> 会被分别转换为 \"a\ss,此后一切工作方式与之前描述的 latin1 案例完全相同。

注意事项(Caveat)

这里还有一个可能偶尔出现的问题(参见《Available Characters with iso-8859-1》)。Latin-1 编码在槽位(slot)0xA5(十进制 165)处提供了日元符号(yen character)。根据上述描述,inputenclatin1 选项会为此字符定义 \textyen 的转换,但 T1 输出编码并未为该符号预留槽位,因此输入 ¥ 会导致 LaTeX 运行时错误(runtime LaTeX error)。此时必须加载一个为 \textyen 提供默认输出的包(package),例如 textcomp。若使用 utf8 输入编码,也会遇到同样的问题。

唯一能被安全输入的字符是那些被输出编码覆盖的字符,或已通过输出编码定义合适渲染方式的字符。

前言结束,回到问题本身

当然以上这些对于使用xe的用户来说并不需要,在用户层面来说,inputenc并不是为xelatex设计的,默认文档编码以及xelatex引擎的读入编码均为UTF-8,这意味着用户并不需要(也不允许)显式设置inputenc;而xelatex原生支持unicode编码,对于这种情况下,要修改字体的设置可以通过fontspec(其内部调用了fontenc)进行调整,因此对于xe用户来说,fontenc也是没有必要被显式调用的。

回到ctex的文档中的说明,关于文档编码,有如下的介绍:

image-20250316115622746

从中我们知道在xelatex以及lualatex中,ctex-kit均强制使用UTF-8选项编码,而pdflatex有两种选择,默认为UTF8,仅仅对于历史遗留文档可使用GBK编码。同时在vscode上新建一个.tex文件的默认编码也为UTF8

下面是一个用于测试的MWE:

\documentclass[<encooding option>]{ctexart}
\usepackage{lipsum,zhlipsum}
\begin{document}

现代社会以海德格尔的一句“一切实践传统都已经瓦解完了”为嚆矢。滥觞于家庭与社会传统的期望正失去它们的借鉴意义。
但面对看似无垠的未来天空,我想循卡尔维诺“树上的男爵”的生活好过过早地振翮。我们怀揣热忱的灵魂天然被赋予对超越性的追求,不屑于古旧坐标的约束,钟情于在别处的芬芳。

\lipsum[1]

\zhlipsum[1]

\end{document}

Claim:下面的测试仅仅基于TeXlive2024windows11 24H2 内部操作版本为26100.2894:

尝试对此遍历下面的情况,列信息的含义为(<.tex文件编码>,<ctex-kit>指定的编码)

(GBK,GBK)(UTF8,UTF8)(GBK,UTF8)(UTF8,GBK)
pdflatex(●'^'●)CASE1(●'◡'●)(●'^'●)CASE3(●'^'●)CASE4
xelatex(●'^'●)CASE2(●'◡'●)(●'^'●)CASE2(●'◡'●) GBK选项无效
lualatex(●'^'●)CASE2(●'◡'●)(●'^'●)CASE2(●'◡'●) GBK选项无效

对于CASE1,编译出现如下错误,\zhlipsum不支持以GBK编码编译,但自己输入的中文可以被正确输出到PDF

! Package zhlipsum Error: The current CJK environment uses "GBK" encoding, but
(zhlipsum)                zhlipsum package has been loaded with the option
(zhlipsum)                "encoding=utf8".
(zhlipsum)                Please check the package options.

Type <return> to continue.
 ...

l.12

?

对于CASE2,编译出现大量字体缺失的警告,同时自己输入的中文出现乱码,zhlipsum表现正常。下面仅为.log的部分示例:

Missing character: There is no � (U+FFFD) in font [lmroman10-regular]:mapping=t
ex-text;!
Missing character: There is no ִ (U+05B4) in font [lmroman10-regular]:mapping=t
ex-text;!
Missing character: There is no � (U+FFFD) in font [lmroman10-regular]:mapping=t
ex-text;!
Missing character: There is no � (U+FFFD) in font [lmroman10-regular]:mapping=t
ex-text;!
Missing character: There is no � (U+FFFD) in font [lmroman10-regular]:mapping=t
ex-text;!
Missing character: There is no � (U+FFFD) in font [lmroman10-regular]:mapping=t
ex-text;!
Missing character: There is no � (U+FFFD) in font [lmroman10-regular]:mapping=t
ex-text;!

对于CASE3,编译出现如下的错误,且无法输出PDF文件

! Package CJK Error: Invalid character code.

See the CJK package documentation for explanation.
Type  H <return>  for immediate help.
 ...

l.5
      Ժ¸һ䡰һʵͳѾ߽ˡΪʸڼͥ...

?
Missing character: There is no  in font cmr10!

! LaTeX Error: Invalid UTF-8 byte "FA.

对于CASE4,编译出现如下错误,我猜是在输入阶段TeX不认识以UTF8编码的“现代社会”,自己输入的文本乱码,且\zhlipsum无法输出内容:

! LaTeX Error: Invalid UTF-8 byte "80.

See the LaTeX manual or LaTeX Companion for explanation.
Type  H <return>  for immediate help.
 ...

l.5 现代社会以海德格尔的一
                                     句“一切实践传统都已经瓦解...

Questions:

1.仅仅作为一个实验记录

2.在CASE2中为何\zhlipsum能正常输出,但自己的中文内容出现乱码

3.现如今(2025年)如果拿到一个使用GBK编码的.tex文件,比较推荐尝试的编译方式是什么?(可以改用CTeX套装编译试试,抑或使用GBK作为option的ctex-kitpdflatex下编译?)

PS.由于我赶着吃饭,上面的内容应该有一些错漏,可以及时留下评论我会做修改。

查看更多

关注者
0
被浏览
412
雾月
雾月 2025-03-17
这家伙很懒,什么也没写!

case 1 是可以正常输出的,需要指定 \usepackage[encoding=gbk]{zhlipsum}

%%% 文件编码为 GBK!
\documentclass[GBK]{ctexart}
\usepackage{lipsum}
\usepackage[encoding=gbk]{zhlipsum}
\begin{document}

现代社会以海德格尔的一句“一切实践传统都已经瓦解完了”为嚆矢。滥觞于家庭与社会传统的期望正失去它们的借鉴意义。
但面对看似无垠的未来天空,我想循卡尔维诺“树上的男爵”的生活好过过早地振翮。我们怀揣热忱的灵魂天然被赋予对超越性的追求,不屑于古旧坐标的约束,钟情于在别处的芬芳。

\lipsum[1]

\zhlipsum[1]

\end{document}

case 2、3、4 都是同一个问题,TeX 不能正确解码 GBK 编码或 UTF8 编码的 bytes 为 Unicode Scalar Value。
可分别观察如下 python 代码的结果:

>>> b'\xd2\xbb'.decode('utf8')
'һ'   ### 看看 case 3 的错误日志
>>> b'\xd2\xbb'.decode('gbk')
'一'
>>> '\ufffd\ufffd'.encode('utf8')
b'\xef\xbf\xbd\xef\xbf\xbd'
>>> '\ufffd\ufffd'.encode('utf8').decode('gbk')
'锟斤拷'

同一段 bytes 以不同的方式解码得到的是不同的 Unicode Scalar Value。如果一段 bytes 可以以 GBK 解码但不能以 UTF8 解码,就会得到错误,反之亦然。如果可以以两种方式解码,那看到的 Unicode Scalar Value 也大概率不是一样的,ASCII 除外。
TeX 看到的总是 bytes,人看到的是 Unicode Scalar Value,如果字体里没有某些 Unicode Scalar Value 的字形,就不能在 PDF 中显示。

对于 case 2,由于 zhlipsum 分别制作了 gbk 和 utf8 两个文件用来保存假文,所以,在这些文件中,文件的编码和 bytes 是匹配的,并且在 case 2 中,总是使用 utf8 这个文件,TeX 也总以 utf8 解码,所以不论 main 文件编码为何,TeX 总能读出假文正确的 Unicode Scalar Value。而 main 文件由于文件编码和解码方式不同,TeX 不能正确读取(解码)。

现在如果有 GBK 编码的 TeX 文件,最好的办法应该是统一转为 UTF8 编码,然后使用 CTeX-kit 默认的编码方式。

1 个回答

撰写答案

请登录后再发布答案,点击登录

发布
问题

分享
好友

手机
浏览

扫码手机浏览