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 目前絕對不支持除梵文和馬拉雅拉姆語之外的任何印度腳本。這就是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 ಶ್ರೀ”之間的區別)可以用以下方式解釋:@烏爾麗克·費舍爾指出在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

相關內容