OS X:UTF8-NFD 中的变音符号会产生“包 inputenc 错误:Unicode 字符 \u8:̈ 未设置为用于 LaTeX”

OS X:UTF8-NFD 中的变音符号会产生“包 inputenc 错误:Unicode 字符 \u8:̈ 未设置为用于 LaTeX”

切换到 OS X 后,我首先要学习的困难之一是许多非 ASCII 字符(例如德语)ü可以在 UTF8 中编码为(至少)两种不同的形式:

  • U+00FC(带分音符的拉丁文小写字母 U):规范化形式 C(近场通信
  • U+0075 U+0308(带组合分音符的拉丁文小写字母 U):规范化形式 D(神经功能缺损

(荣耀细节全部描述这里

基本上,当今所有操作系统和应用程序都使用近场通信只有 Mac OS X 除外,其中一些应用程序(例如 OpenOffice 或 HFS+ 文件系统)使用神经功能缺损结果是,如果你从这样的应用程序中复制粘贴一些文本(例如,ls)到你的 LaTeX 文档中,所有内容看起来美好的。

\documentclass{article}
\usepackage[utf8]{inputenc} % comment out for lualatex/xelatex
\usepackage[T1]{fontenc}    % comment out for lualatex/xelatex

\begin{document}
äöüÄÖÜß
\end{document}

但是,使用 pdflatex 编译时:

! Package inputenc Error: Unicode char \u8:̈ not set up for use with LaTeX.

关于 unicode 问题,一个常见的答案是“使用 lualatex/xelatex”。然而,这似乎也无济于事。如果使用 lualatex/xelatex 进行编译,输出不包含变音符号:

在此处输入图片描述

问题:inputenc带有的包显然[utf8]无法处理 NFD。是否可以扩展它以便上面的代码可以编译?


警告

请注意,如果将 MWE 从此处复制并粘贴到新文档中,实际上编译。显然我的浏览器或 SE 网站透明地将 NFD 转换为 NFC. (对于 Safari 和 Crome似乎确实如此;我也尝试过 Firefox,但没有成功)。我还没有弄清楚如何在这里用 NFD 提供一些文本。


附记:关于 HFS+ 的一些额外背景知识

我第一次遇到这个问题是在尝试将ls命令的输出放入我的 LaTeX 文档时:OS X 中许多问题的根源在于 HFS+ 文件系统使用(出于一些非常奇怪的原因)NFD。更糟糕的是:HFS+透明地将其收到的所有 NFC 字符在内部转换为 NFD 输入。实际上,这意味着您得到的文件名与您输入的文件名不同:如果您创建一个文件ü(键盘提供 NFC),然后列出目录(文件系统提供 NFD),名称看起来相同,但实际上不同。简短的说明测试(在空目录中执行):

$ echo ü; echo ü | xxd; touch ü; ls; ls | xxd
ü
0000000: c3bc 0a                                  ...
ü
0000000: 75cc 880a                                u...

ls这就是为什么很多工具(unison、svn、git 等)或 bash 的制表符补全在 OS X 上对包含变音符号的文件名产生阻碍的原因 – 并且您无法在 LaTeX 文档中直接使用输出。

答案1

(请参阅最后的可能解决方案。)

XeLaTeX 输入中的 NFC 和 NFD UTF-8 格式调查

xelatex 几乎处理 NFD 表格几乎开箱即用。您将需要加载该xltxtra包,在使用 XeLaTeX 时您可能总是希望加载该包。

下面是创建测试文档的示例 bash 脚本 ( mkutest.sh):

#! /bin/bash
(
  TEXT="åäöüÅÄÖÜß"
  cat <<'EOF'
\documentclass{article}
\usepackage{xltxtra}
\begin{document}
EOF
  echo
  uconv -f utf-8 -t utf-8 -x nfc <<<"UTF-8-NFC: $TEXT"
  echo
  uconv -f utf-8 -t utf-8 -x nfd <<<"UTF-8-NFD: $TEXT"
  echo
  cat <<'EOF'
\end{document}
EOF
) > utest.tex

该脚本使用uconv(来自重症监护室,参见下面的注释 1)来创建相同文本的两种表示形式(NFC 和 NFD)并添加 XeLaTeX 前/后同步码。此脚本应可以“安全地”从网页复制,因为它使用转换器,并且输入到其中的文本可以是任何 UTF-8 格式。(有关不依赖于 的版本,请参阅下面的注释 2。uconv

创建的文件如下所示(utest.tex):

\documentclass{article}
\usepackage{xltxtra}
\begin{document}

UTF-8-NFC: åäöüÅÄÖÜß

UTF-8-NFD: åäöüÅÄÖÜß

\end{document}

(注意:如果只是从网上复制,可能不会产生所需的文件。请参阅问题中的警告。)

通过 XeLaTeX 运行的结果是包含以下内容的 PDF:

在此处输入图片描述

其中两条线才不是看起来完全一样(即使标签除外)。第一行的重音看起来没问题,但第二行大写字母的重音严重错位。

因此,尽管 XeLaTeX 可以处理 NFD 格式,但它可能无法正确处理......

如果\usepackage{xltxtra}省略,PDF 如下所示:

没有 xltxtra 包的示例

这证实了问题中 XeLaTeX 的示例使用。此外: 注意什么也没有出现在第一行,而ß第二行中没有。这是因为加载的字体没有字形来呈现它。加载xltxtrafontspec,默认情况下会加载字体“Latin Modern”。如果没有这个,只会加载旧字体,这根本无法很好地处理 unicode 文本。

我测试了不同的字体(使用命令加载的系统字体fontspec\setmainfont{<name of font>}。结果有些不同。对于所有具有所需字形的字体,第一行看起来都是正确的。然而,第二行可能会以不同的形式出现。例如,在基本字母后面加上重音符,好像它们是非组合的;或者缺失字形框在基本字母之后...

正如 Khaled 所言,XeTeX 可以将其输入标准化为 NFC。添加\XeTeXinputnormalization=1到前导码中,在读取任何非 NFC 文本之前,仍然使用\usepackage{xltxtra}和/或其他方式设置适当的字体,输出为:

自动 NFC 规范化示例

这次看起来完全一样(除了标签)。


该怎么办?

如果使用 XeTeX,\XeTeXinputnormalization=1肯定是一个解决方案。只需记住您必须正确设置字体。

另一种方法适用于所有(?)支持UTF-8 NFC文本输入的程序,需要事先转换输入文件。

按摩例如,可以使用 NFC 格式uconv重症监护室参见下面的注释 1 ),正如我在MWE 生成器多于。

$ uconv -o outfile.tex -f utf-8 -t utf-8 -x nfc infile.tex

(这也适用于 UTF-16 编码 - 以及其他编码。只需将-f) 和-t)选项适当。)

免责声明:使用此命令风险自负。请务必保留原始文件,直到您可以验证结果为止。

应该大概注意安全可在任何 (7 位) ASCII 或 UTF-8 编码的 tex 文件上运行。如果文件已经是 NFC,则转换不会改变任何内容,因为它是幂等的。仅包含 7 位 ascii 的文件已经是 NFC,因为 7 位 ASCII 是 UTF-8 的子集,不包含可能使文本非 NFC 的组合字符。


笔记

  1. uconv来自的实用程序重症监护室在我的 Ubuntu 12.04 64 位上,它位于 libiuc-dev 软件包中。
    (我认为它是例子对于 ICU4C 库,但我在主页上快速搜索找不到有关它的任何信息。我​​有点困惑……)

  2. 正如 David 在评论中所要求的那样我已经制作了一个版本MWE 生成器不依赖于uconv

    #!/bin/bash
    (
      echo '\documentclass{article}'
      echo '\usepackage{xltxtra}'
      echo '\begin{document}'
      echo
      echo -e 'UTF-8-NFC: \xc3\xa5\xc3\xa4\xc3\xb6\xc3\xbc\xc3\x85\xc3\x84\xc3\x96\xc3\x9c\xc3\x9f'
      echo
      echo -e 'UTF-8-NFD: \x61\xcc\x8a\x61\xcc\x88\x6f\xcc\x88\x75\xcc\x88\x41\xcc\x8a\x41\xcc\x88\x4f\xcc\x88\x55\xcc\x88\xc3\x9f'
      echo
      echo '\end{document}'
    ) > utest.tex
    

    此版本仅依赖于echo -e解释\xHH (而不echo依赖于解释-e)。

    我保留了另一个版本(上面的正文),因为它允许轻松更改示例文本。

    对于感兴趣的人,十六进制转义由 NFC 生成 uconv -x '[:Cc:]>; ::nfc;' <<<"$TEXT" | hexdump -v -e '/1 "%02x "' | sed -e 's/[[:xdigit:]][[:xdigit:]]/\\x\0/g; s/ //g' ,对于 NFD 也同样如此。

答案2

支持在 中组合字符确实很困难inputenc。该包通过使用宏定义使所有非 ASCII 字符“活动”来工作。对于T1和类似的 8 位编码,此定义只是直接排版该插槽中的字符,但对于 UTF-8,它会触发一个小解析器,向前查看 UTF8 多字节编码的接下来几个字节,以确定需要哪个字符。经典 TeX 在找到字符标记后立即排版该标记并将其放在正在构建的水平列表中。但需要注意的重要一点是,水平列表不能解构没有或\lastchar的原始类似物。这意味着当您找到组合重音字符时,基数已被排版到列表中并变得不可访问。\lastskip\unskip

在某些情况下,您可以使用一些 TeX 技巧让 TeX 提前扫描并找到这些内容,但它们本质上很脆弱,往往会破坏任何其他已加载的包。将文件通过 Unicode 规范化工具并将其恢复为 NFC 格式要可靠得多。

或者当然使用支持 unicode 的 TeX,例如 luatex 或 xetex。

答案3

显然,LaTeX 本身没有简单的解决方案来解决这个问题。因此,如果不想使用 XeLaTeX \XeTeXinputnormalization=1,如 Khaled 所建议的那样,最好的方法可能是将输入(源)标准化为 NFC,如 David 所建议的那样。

这是我过去一直在做的事情。然而,这并不像听起来那么容易:实际上很少有工具可以进行这种标准化。特别是iconv它背后的系统库,大多数编辑器(包括 vim)都使用它进行编码转换,不是NFD 功能。因此,下面我将介绍进行转换的工具和方法。我创建了这个答案社区 wiki,因此如果您了解更多工具,请为其做出贡献。

穷人的方法(OS X):文本编辑

  • 在 TextEdit 中打开文档(或直接粘贴 NFD 文本)
  • 将 TextEdit 设置为纯文本格式 (格式->制作纯文本
  • 将文档保存为其他编码(非 UTF-8) 包含 NFD 中表示的字符。(例如,对于德语变音符号,您可以选择西方(ISO 拉丁语 9))。
  • 再次以 UTF-8 编码保存文档(或者直接将文本从 TextEdit 复制到 LaTeX 文档中)。生成的编码现在是 NFC。

这种方法的优点是不需要第三方工具。但是,它需要一些手动步骤,并且过渡到一些中间编码可能会丢失不常见的字符。

真正的转换工具

uconf

uconv实用程序是我迄今为止发现的最完整的转换器。它实际上是 ICU 项目的一个示例应用程序,该项目是一套广泛的开源库和 API,用于处理针对软件开发人员的 unicode 问题。它似乎在某些 Linux 发行版中可用,但不幸的是它还不是 OS X 或 MacPorts 的一部分。因此,您必须从来源,但它运行得非常完美。


2015年3月更新:同时,uconv在大多数平台上都可以轻松获取:在 MacPorts 上通过安装包icu,Homebrew 用户安装icu4c,而在 Ubuntu 上该包名为icu-devtools


一旦uconf可用,您就可以像使用它一样iconv,但您有一个额外的-x命令行选项来指定规范化形式。在 bash shell 中执行以下命令,将其转换paper.texpaper-nfc.tex

uconv -f utf-8 -t utf-8 -x NFC paper.tex >paper-nfc.tex

OS X 的一个有用的单行代码如下。它将剪贴板的内容重新编码为 NFC:

pbpaste | uconv -f utf-8 -t utf-8 -x NFC | pbcopy

今天,我在将内容(例如 OpenOffice 文档的输出ls或文本块)复制到 LaTeX 文档时会使用它。只需在复制后、粘贴前执行上述代码即可。(为了更加方便,可以创建运行此代码的 Automator 工作流,并为该工作流分配键盘快捷键。)

在线规范化工具

还有一种可能性是使用一些在线 Web 服务进行转换。但是,到目前为止,我只找到了这一个:

答案4

以防有人用python编写文档。 确保将其保存为UTF-8:

with open(f'{filename}.tex', 'w', encoding='utf-8') as tex_file:
    tex_file.write(latex_document)

相关内容