XeTeX 文本布局对空格非常敏感

XeTeX 文本布局对空格非常敏感

我在脚本中有一个文档需要复杂的文本布局我相信这应该可以在 XeTeX 中工作。但我得到了令人惊讶的结果:

\documentclass{article}
\usepackage{fontspec}

\tracinglostchars=2 % https://tex.stackexchange.com/a/41235/48
\def\testtext{R ಶ್ರೀವತ್ಸ \quad Rಶ್ರೀವತ್ಸ}

\begin{document}

\fontspec{Arial Unicode MS} \testtext

\fontspec{Noto Sans Kannada} \testtext

\fontspec{Noto Serif Kannada} \testtext

\fontspec{Kedage} \testtext

\end{document}

编译后xelatex得到:

输出

对于那些看不懂脚本的人来说,左边的内容(输入的R ಶ್ರೀವತ್ಸR 后面有一个空格)是正确的,而右边的内容(输入的文本相同,但是 R 后面没有空格)则不正确。

我理解输出中的“方框”:这是因为所选的卡纳达语字体中没有 R 字符。(由于 ,在终端中打印了一条有关此内容的消息\tracinglostchars=2。)

问题:为什么省略空格会导致输出错误?即使没有空格,如何才能使程序正常运行?

据我了解,在 XeTeX 中,文本布局(又称文本渲染,又称文本整形)由 HarfBuzz 库提供,许多其他应用程序都使用该库,应该能够很好地处理此文本。在 LuaTeX 中,他们试图避免系统依赖性,并希望自己实现一切(使用 Lua 代码),这可能低估了文本布局的复杂性,而且无论如何,LuaTeX 目前完全不支持除 Devanagari 和 Malayalam 之外的任何印度语脚本。因此,这就是lualatex上述文件生成的内容:

路拉泰克斯

(至少我明白它始终是错误的!)


编辑:感谢@cfr 在下面给出的回答,我知道我应该怎么做才能解决实际问题:在加载字体时指定脚本(例如\fontspec{Noto Sans Kannada}[Script=Kannada]或她回答中的更好方法)。因此,可以解决这个问题;唯一剩下的问题是:这是怎么回事?

值得一提的是,这里有一个可以重现该问题的最小纯 XeTeX 文件(使用xetex而不是进行编译xelatex):

\font\notosansnone="Noto Sans Kannada"
% \font\notosanskndt="Noto Sans Kannada:script=knd2"
\font\notosansknda="Noto Sans Kannada:script=knda"

\def\testtext{R ಶ್ರೀ Rಶ್ರೀ}

{\notosansnone \testtext} (No script)

% {\notosanskndt \testtext} (knd2)

{\notosansknda \testtext} (knda)

\bye

答案1

我没有第一种或最后一种字体。但是,Polyglossia 对我来说工作正常。(我认为它可能也适用于正确的字体配置,但我这样做是因为这可能是你最终想要的。)

\documentclass{article}
\usepackage{polyglossia}
\setmainlanguage{kannada}
\setotherlanguage[variant=british]{english}
\newfontfamily\kannadafont{Noto Serif Kannada}[Script=Kannada]
\newfontfamily\kannadafontsf{Noto Sans Kannada}[Script=Kannada]
\tracinglostchars=2 % https://tex.stackexchange.com/a/41235/48
\def\testtext{R ಶ್ರೀವತ್ಸ \quad Rಶ್ರೀವತ್ಸ}

\begin{document}

% \fontspec{Arial Unicode MS} \testtext

\testtext

\sffamily \testtext

% \fontspec{Kedage} \testtext

\end{document}

字体配置

答案2

(分享我对这一切的理解。)

解决方案

一、问题的解决方案:

  • 作为@cfr 的回答指出,我应该使用[Script=Kannada]这种字体,如fontspecpolyglossia手册中所述。当使用它时,一切都按预期工作:无论有没有空格,整个文本都会按照卡纳达语脚本的需要进行渲染。
  • 此外,我们实际上不希望在卡纳达语脚本中呈现非卡纳达语字符(例如 R):R必须将不同脚本的字符标记为不同的语言或至少不同的字体(请参阅下文了解如何执行此操作)。

那么,这是 XeTeX 或它使用的某个库中的错误吗?不,我认为这是用户错误。不过,当单词之间有空格时(无需指定脚本),一切都可以正常工作,这一事实可能使这种用户错误更有可能发生。

解释

如何解释这种因空间不同而产生的行为差异(到底是怎么回事)?这种行为可以在 XeTeX 中改变吗?我发现了以下内容。

XeTeX 用于文本布局的库,即哈夫巴兹(在 Firefox、Chrome、LibreOffice 等中使用,请参阅Harfbuzz 是什么?) 附带了一个名为的命令行程序hb-view,可以使用字体和文本字符串来调用它。使用它我得到了以下输出:

  • hb-view NotoSansKannada-Regular.ttf "ಶ್ರೀ"--script=knda

    没有脚本,没有空间 带有脚本、空间

  • hb-view NotoSansKannada-Regular.ttf " ಶ್ರೀ"--script=knda

    无需脚本,以空格开头 使用脚本,以空格开头

  • hb-view NotoSansKannada-Regular.ttf "Rಶ್ರೀ"--script=knda

    无需脚本,以 R 开头,无空格 使用脚本,以 R 开头,无空格

  • hb-view NotoSansKannada-Regular.ttf "R ಶ್ರೀ"--script=knda

    无需脚本,以 R 和空格开头 使用脚本,以 R 和空格开头

这表明,如果任何一个第一个非空格字符来自正确的脚本,或者脚本已明确指定。

因此,XeTeX 中看到的行为(“Rಶ್ರೀ”和“R ಶ್ರೀ”之间的区别”)可以通过以下方式解释@Ulrike Fischer指出XeTeX 伴侣

XeTeX 的方法如下:

  • 排版过程收集字符(单词)的运行,这些字符的宽度通过系统库的 API 获取 […] 以确定宽度,

  • XeTeX 段落是单词节点分隔胶水

因此,XeTeX 的排版引擎放置的是单词而不是字形,后者由字体渲染引擎绘制。

(上面的“系统库”和“字体渲染引擎”现在是 HarfBuzz(感谢哈立德·霍斯尼);他们以前是ICU。)所以

  • 使用“Rಶ್ರೀವತ್ಸ”,XeTeX 要求 HarfBuzz 将整个字符串渲染为一个单元,但失败了(如上面的 hb-view 实验所示),因为它既不是以所需脚本中的字符开头,我们也没有正确指定脚本,而

  • 使用“R ಶ್ರೀವತ್ಸ”,XeTeX 分别向 HarfBuzz 询问两个单词,在这种情况下,第二个单词被正确呈现(即使我们没有指定脚本),因为它以正确脚本中的字符开头。

但最好不要依赖这种猜测,而是明确指定脚本。

使用两个脚本

为了让两个脚本都能顺利运行,我们应该指定 R 等字符属于不同的语言。我们可以通过将 改为 来实现这一点。\textenglish{R}ಶ್ರೀವತ್ಸ但是Rಶ್ರೀವತ್ಸ,如果我们不想更改输入,可以使用ucharclasses包裹

由于某种原因,我无法让它工作,所以我只能手动操作(参考示例texdoc xetex和一个邮政来自的作者ucharclasses,并将 255 更改为 4095,例如这个答案):

\documentclass{article}
\usepackage{fontspec}
\usepackage{polyglossia}

\newfontfamily\kannadafont{Noto Serif Kannada}[Script=Kannada]
\newfontfamily\englishfont{Georgia}
\setdefaultlanguage{kannada}
\setotherlanguage{english}

\XeTeXinterchartokenstate = 1   % Enable the character classes functionality

\newXeTeXintercharclass \CharEnglish
\XeTeXcharclass `R = \CharEnglish

\XeTeXinterchartoks 0 \CharEnglish = {\selectlanguage{english}}
\XeTeXinterchartoks 4095 \CharEnglish = {\selectlanguage{english}}
\XeTeXinterchartoks \CharEnglish 0 = {\selectlanguage{kannada}}
\XeTeXinterchartoks \CharEnglish 4095 = {\selectlanguage{kannada}}

\begin{document}

R ಶ್ರೀವತ್ಸ \quad Rಶ್ರೀವತ್ಸ

\end{document}

每次我们在英文字符(仅限R上方)与单词边界(4095)或常规(未指定为英文)字符(0)之间移动时,语言都会发生变化。

对于我的原始文档,为了处理所有英文字符,我编写了一个循环来执行相当于

\XeTeXcharclass `R = \CharEnglish

对于字母表中的每个大写字母和小写字母:

\newcount\tmpchar
\tmpchar = `A
\loop
  \ifnum \tmpchar < `[          % [ comes just after Z
    \XeTeXcharclass \tmpchar = \CharEnglish
    \XeTeXcharclass \lccode \tmpchar = \CharEnglish
    \advance \tmpchar by 1
\repeat

相关内容