Текстовая раскладка 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] или лучший способ в ее ответе). Таким образом, возможно решить проблему; единственный оставшийся вопрос:Что происходит?

И, если это имеет значение, вот минимальный файл plain-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]для этого шрифта, как описано в fontspecи polyglossiaруководствах. И когда он используется, все работает так, как и ожидалось: с пробелом или без, весь текст отображается так, как и положено для письма каннада.
  • Кроме того, мы на самом деле не хотим, чтобы символы, не относящиеся к языку каннада, такие как R, отображались в письменности каннада: символы, имеющие другую письменность, такие как R, Rдолжны быть помечены как относящиеся к другому языку или, по крайней мере, к другому шрифту (см. ниже, как это сделать).

Так это ошибка, либо в XeTeX, либо в какой-то используемой им библиотеке? Нет, я бы сказал, что это ошибка пользователя. Тем не менее, тот факт, что все работает нормально, когда между словами есть пробелы (без необходимости указывать скрипт), возможно, делает эту ошибку пользователя более вероятной.

Объяснение

Чем объясняется это расхождение в поведении в зависимости от пространства (что именно происходит)? И можно ли изменить это поведение в XeTeX? Я обнаружил следующее.

Библиотека, используемая XeTeX для верстки текста, а именноHarfBuzz(который используется в 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 (благодаряХалед Хосни); раньше они были в отделении интенсивной терапии.) Так что

  • с «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

Связанный контент