LINUX.ORG.RU

X Logical Font Description и HiDPI

 , , xlfd, ,


1

3

Всем здравствуйте.

TL;DR: Похоже, что X-сервер отдаёт серверные шрифты в фиксированном разрешении 100 dpi, а не в разрешении, текущем для сервера.

Немного теории. Есть серверные шрифты, которые X-клиенту по сети (через TCP либо UNIX socket) «отдаёт» либо сам X-сервер, либо отдельный X Font Server (один или несколько). В отличие от привычных всем клиентских шрифтов (Xft, GTK 2+, Qt 2+), «серверный» backend (он ещё называется core X font backend) не поддерживает антиалиасинг, но поддерживает сетевую прозрачность (т. е. по сети перегоняются bitmap’ы без альфа-канала). На уровне приложения серверные шрифты задаются не в виде XftFontStruct (что чаще всего вырождается в привычное нам DejaVu Sans Mono 12), а в виде XLFD. Если мы говорим о локальной машине, то один и тот же файл шрифта может быть зарегистрирован сразу в двух подсистемах и доступен и современным приложениям на базе GTK и Qt, и «суровому легаси» (Xt, Athena, Motif, GTK 1.2, Qt 1.x).

Исторически серверные шрифты были растровыми (*.pcf), а для растра существует такое понятие, как его собственное, растра, разрешение. Поэтому в XLFD есть такие поля, как RESOLUTION_X и RESOLUTION_Y. Чтобы растровый шрифт выглядел на экране адекватно, разрешение растра должно быть близким к разрешению экрана, поэтому растровые шрифты обычно поставлялись с собственным разрешением в 75 dpi и 100 dpi (каталоги /usr/share/fonts/X11/75dpi и /usr/share/fonts/X11/100dpi – как раз про это). Так,

-bitstream-charter-bold-r-normal--12-120-75-75-p-75-iso8859-1
-bitstream-charter-bold-r-normal--17-120-100-100-p-107-iso8859-1

– это один и тот же шрифт размером 12 pt, имеющий характерный размер символа

  • 12 px при разрешении 75 dpi, и
  • 17 px – при 100 dpi, соответственно.

Но, помимо растровых шрифтов, есть ещё векторные (TrueType, OpenType, Adobe Type 1), которые мы все любим и которые можно безбоязненно масштабировать. Некоторые реализации X-сервера (напр., XSun) поддерживали ещё формат Adobe Type 3, в котором шрифт описывался с помощью языка PostScript (Тьюринг-полного, кстати).

К векторным шрифтам понятие разрешения растра, естественно, неприменимо, поэтому в полях RESOLUTION_X и RESOLUTION_Y я могу запрашивать 0, а могу запрашивать «звёздочки» (*), и, в теории, X-сервер должен отдать мне нужный шрифт. Об этом напрямую говорится в статье Arch Wiki по ссылке выше:

Scalable fonts were designed to be resized. A scalable font name, as shown in the example below, has zeroes in the pixel and point size fields, the two resolution fields, and the average width field.

To specify a scalable font at a particular size you only need to provide a value for the POINT_SIZE field, the other size related values can remain at zero. The POINT_SIZE value is in tenths of a point, so the entered value must be the desired point size multiplied by ten.

Так, любой из этих запросов должен мне вернуть шрифт Courier New размером 12 pt в разрешении X-сервера:

-monotype-courier new-medium-r-normal--*-120-*-*-m-*-iso10646-1
-monotype-courier new-medium-r-normal--0-120-0-0-m-0-iso10646-1

Или, по кр. мере, я так думал. Штука в том, что, пересев с мониторов с разрешением в 96…115 dpi за 4k-монитор с разрешением в 162 dpi, я заметил, что мои заботливо выбранные векторные шрифты внезапно стали мелковаты.

И выяснилось, что, если явно не указывать RESOLUTION_X и RESOLUTION_Y равными 162 (а никто в здравом уме этого не делает – это пришлось бы каждый раз при изменении монитора переписывать сотни строк Xresources), то X-сервер по умолчанию отдаёт шрифт в разрешении 100 dpi вместо 162. Разница между 17-ю пикселями и 27-ю (пресловутый коэффициент 1.62 = 162 / 100) достаточно заметна. Вот пример для современного Debian 10 (сверху для сравнения дан снимок font-manager, отрисовывающего клиентский шрифт): Debian 10, Courier New 12pt.

Я было подумал, что это следствие постепенного выпиливания устаревших подсистем из X11, но в Debian Woody, выпущенном в 2002 году и имеющем ядро 2.2, увидел ровно то же самое: Debian 3, Courier New 12pt. Разве что сам шрифт старый Debian отрисовывает «чище», видимо, выполняя хинтинг на серверной стороне, до пересылки bitmap’ов по сети.

Проблема существовала всегда и в равной степени затрагивает все векторные шрифты (TrueType, OpenType, Type 1).

Собственно, вопрос. Старожилы, посоветуйте – есть ли способ, не зашивая жёстко в пользовательские настройки для каждого отдельного ресурса разрешение X-сервера, обойтись меньшей кровью, чем рекомендует автор статьи Sharing Xresources between systems? Поддержка препроцессора в xrdb, увы, всё-таки достаточно убога.

Можно ли решить что-то на уровне глобальной конфигурации собственно X-сервера?

Или меня спасёт m4?

Cast @Zubok.

Update. По здравом размышлении я пришёл к выводу, что схема XLFD сама по себе ущербна. Дело в том, что X-ресурсы (речь в первую очередь о шрифтах) для каждой конкретной программы могут существовать как на стороне сервера (всё то, что даётся на вход xrdb), так и на стороне клиента (см. XUSERFILESEARCHPATH). И настройки, скажем, шрифта для пресловутого xterm должны храниться где-то в одном месте – либо на клиенте, либо на сервере. А XLFD включает в себя и серверные параметры (RESOLUTION_X и RESOLUTION_Y), и клиентские (CHARSET_REGISTRY и CHARSET_ENCODING – ведь потенциально удалённый, «сетевой» X-клиент может быть запущен в окружении и с региональными настройками, отличными от окружения и настроек X-сервера) – и вот это как раз-таки и неправильно. При разработке Xft Кит Пакард всё сделал по уму: имя и размер шрифта в пунктах (DejaVu Sans Mono:size=12) задаёт X-клиент, а X-ресурс Xft.dpi является частью состояния X-сервера.

Возможно, мою проблему удастся решить с помощью серверных псевдонимов шрифтов (font aliases, файлы fonts.alias).

★★★★★

Последнее исправление: Bass (всего исправлений: 7)

Спасибо, всегда хотел узнать, как делается плюмбус.

CYB3R ★★★★★
()

Занятный вопрос. Поверхностное ковыряние исходников X-ов пока что показало мне, что при использовании символа * в полях resx, resy в итоге этим параметрам назначается значение -1. Собственно оно и передается в функцию FT_Set_Char_Size(…) в качестве параметров разрешения по вертикали и горизонтали. Что делает FreeType в таком случае, не могу сказать. Может для FreeType можно задать какую-нибудь переменную окружения, в которой прописывется разрешение по умолчанию, и все волшебным образом заработает как надо?

zloy_starper ★★★
()
Ответ на: комментарий от Bass

Я,вероятно,несколько поспешил. Все-таки, скорее всего во FreeType передаются 0, а не -1. Соответственно FreeType использует разрешение по умолчанию 96 dpi. В коде там много чего наворочено, с наскоку трудно докапаться до самой сути. Но, вроде бы, X server не пытается определять текущее разрешение и привязывать его к шрифтам со *.

zloy_starper ★★★
()

Звездочка в полях resx и resy в XLFD раскрывается на сервере в 75 или 100 из-за вот такой незамысловатой функции: https://gitlab.freedesktop.org/xorg/xserver/-/blob/master/dix/dixfonts.c#L1843

Можешь наложить патчик, чтобы выбирались другие разрешения шрифта, исходя из разрешения скрина. Например, 96, 120, 144, 168, 192.

iliyap ★★★★★
()
Ответ на: комментарий от iliyap

XXX - we’ll want this as long as bitmap instances are prevalent so that we can match them from scalable fonts

Дичь какая.

И это, блин, называется культура программирования. Руки вырвать, спички вставить.

Илья, огромное спасибо за ответ!

На самом деле, я уже нашёл четыре варианта, которые с натяжкой можно назвать решениями:

  1. Вручную отрастеризовать TTF-шрифты в формат PCF в разрешении 162 dpi с шагом размера в 1 пункт (десятыми долями пункта можно пренебречь) и положить в FontPath каталог с ворохом полученных PCF вместо (или раньше) исходного каталога с TTF-шрифтами. Тогда при поиске сначала будут находиться шрифты в разрешении именно 162 dpi. Этот процесс, кстати, можно автоматизировать и запускать из ~/.xinitrc, предварительно обработав вывод xdpyinfo и получив актуальное разрешение X-сервера.
  2. Аналогично, сделать копию исходного каталога с TTF-шрифтами, sed-ом модифицировав в нём fonts.dir и fonts.scale (заменив 0-0 на 162-162), и положить эту копию в FontPath вместо или раньше оригинала.
  3. Использовать на клиентской стороне псевдонимы шрифтов (fonts.alias), которые на X-сервере разрешались бы в фактические имена шрифтов, содержащие актуальное разрешение X-сервера.
  4. Наконец, не использовать статические X-ресурсы на стороне X-клиента, а генерировать их из шаблонов, подставляя в качестве параметра текущее разрешение X-сервера.
Bass ★★★★★
() автор топика

Ой, я вот мало что помню по этой теме и у меня нет сейчас в работе HiDPI моников, поэтому я не озадачивался проблемой. Сейчас у меня (да и у всех почти, я полагаю) server-side fonts рендерятся самим сервером, хотя вот в 2000-х у меня всегда была рабочей конфигурация с X Font Server. Но потом я его убрал и ничего не почувствовал. Однако в связи с твоим вопросом, я вспомнил про него и глянул в man. В конфигурации xfs есть такой параметр:

default-resolutions (list of resolutions)
    Resolutions the server supports by default. This information may be used as a hint for pre-rendering, and substituted for scaled fonts which do not specify a resolution. A resolution is a comma-separated pair of x and y resolutions in pixels per inch. Multiple resolutions are separated by commas. 

Пример:

default-resolutions = 100,100,75,75

Я так понимаю, что сейчас хочется хотя бы единую точку конфигурации получить. Вот попробуй. Вдруг чо. Мне сейчас городить setup c xfs не хочется.

Сам-то сервер правильно знает свой реальный DPI или там умолчательный 96 dpi стоит? Что говорит

xdpyinfo | grep "resolution"
Zubok ★★★★★
()
Последнее исправление: Zubok (всего исправлений: 2)
Ответ на: комментарий от Zubok

Евгений, спасибо за ответ!

Да, разрешение сервера в порядке – т. е. он сам «знает», что у него 162 dpi (xrandr --dpi 162).

Что касается X Font Server – то да, я помню про этот конфигурационный параметр, и, конечно, у обычного X-сервера такого параметра тупо нет.

Штука в том, что в Debian с некоторых пор собирают libxfont2 (от которой зависит, в т. ч., и X-сервер) без поддержки внешних X Font Server’ов – т. е. FontPath не может быть ни tcp/external-font-server:7100, ни даже unix/:7100.

На это натыкаешься при попытке подключения любого внешнего X Font Server’а посредством xset fp+, когда в ответ «прилетает» X11 BadRequest.

У меня в планах взять последние релизы libxfont1, libxfont2 и xfs из репозиториев X.org (потому что в том же Debian обновлять их забили) и пересобрать по-человечески.

Ещё раз спасибо.

Bass ★★★★★
() автор топика
Последнее исправление: Bass (всего исправлений: 2)
Ответ на: комментарий от Bass

Кстати, что касается масштабирования legacy во времена HiDPI, то Keith Packard разместил у себя кое-какие мысли и патчи в его дереве.

https://keithp.com/blogs/window-scaling/

Но это просто масштабирование путем апдейта Composite Extension. Это совсем не про то, что в топике, но релевантно.

Zubok ★★★★★
()
Ответ на: комментарий от Bass

А разве XRandR в режиме fractional DPI scaling решает не ту же самую задачу?

Не ту. В xrandr масштабирование всего изображения сразу, глобально, а тут масштабирование на уровне отдельного клиентского окна средствами X Server. Идея в том, чтобы масштабировать изображение окна клиента и организовать трансляцию устройств ввода.

Zubok ★★★★★
()
Ответ на: комментарий от Zubok

масштабирование на уровне отдельного клиентского окна средствами X Server.

А, понял, спасибо.

Bass ★★★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.