2025, Oct 06 05:16

Правильное подключение пользовательских шрифтов в Arcade через внутреннее имя pyglet

Разбираем, почему .ttf в Arcade рендерится запасным шрифтом, как узнать внутреннее имя в pyglet/FreeType и корректно передать его в font_name. Примеры кода.

Загрузка пользовательского .ttf в проект на Arcade может сбивать с толку: файл шрифта подключается без ошибок, но текст рендерится запасным, например Segoe UI. Привычные версии — имя файла, подпись из просмотрщика шрифтов или строки метаданных — не обязательно совпадают с идентификатором, которым Arcade/pyglet реально пользуются при отрисовке. Важен внутренний идентификатор шрифта, встроенный в сам файл.

Постановка проблемы

Вы загружаете файл шрифта и при создании текстового объекта в Arcade передаёте, казалось бы, корректное имя, но всё равно получаете подмену:

arcade.load_font("Assets/Fonts/CopperplateGothic.ttf")
self.label_actions = arcade.Text(
    "Actions",
    font_name="CopperplateGothicStd-32BC",
    color=accent_color,
    font_size=32,
    x=0,
    y=btn_y + btn_h + 20,
    batch=self.title_batch,
)
print(self.label_actions.font_name)

Попытки использовать варианты вроде подписи в просмотрщике шрифтов Windows (например, «Copperplate Std 32 BC»), значения свойства файла («CopperplateGothicStd-32BC») или имя файла с/без расширения не помогают. При этом выбор известного системного шрифта вроде «MS Comic Sans» работает как ожидается, что говорит о корректной работе самого механизма поиска шрифта.

Что на самом деле происходит

Arcade использует под капотом pyglet для управления шрифтами. Строка, которую вы передаёте в font_name, сопоставляется с внутренним именем шрифта, которое парсит pyglet, а не с произвольными отображаемыми именами или названиями файлов. Это внутреннее имя доступно через основанный на FreeType загрузчик pyglet и может отличаться от того, что показывают интерфейсные инструменты. Если передать любое другое имя, pyglet не сможет разрешить начертание и тихо переключится на запасной шрифт.

Посмотреть внутреннее имя можно напрямую через FreeTypeFace в pyglet:

import pyglet.font.freetype
font_path = "Copperplate-Gothic-Std-32-BC.ttf"
face = pyglet.font.freetype.FreeTypeFace.from_file(font_path)
print(face.name)

Для проверенного файла это выводит «Copperplate Gothic Std». Именно это значение pyglet использует при выборе шрифта, и тот же механизм применяется в arcade.load_font (внутри он опирается на pyglet.font.add_file).

Можно убедиться, что это имя подходит, напрямую в pyglet:

import pyglet
font_path = "Copperplate-Gothic-Std-32-BC.ttf"
pyglet.font.add_file(font_path)
family_name = "Copperplate Gothic Std"
resolved = pyglet.font.load(family_name)
print(resolved.name)

Исправляем использование в Arcade

Как только у вас есть внутреннее имя семейства, передавайте его в font_name при создании текста в Arcade. Ниже минимальный пример, который подключает файл и рендерит именно с этим именем:

import arcade
WIN_W = 400
WIN_H = 300
APP_TITLE = "Example"
font_path = "Copperplate-Gothic-Std-32-BC.ttf"
family_name = "Copperplate Gothic Std"
class Demo(arcade.Window):
    def __init__(self):
        super().__init__(WIN_W, WIN_H, APP_TITLE)
        arcade.load_font(font_path)
        self.lbl = arcade.Text(
            "Actions",
            font_name=family_name,
            font_size=32,
            x=WIN_W // 2,
            y=WIN_H // 2,
            anchor_x="center",
            anchor_y="center",
        )
        print(self.lbl.font_name)
    def on_draw(self):
        self.clear()
        self.lbl.draw()
if __name__ == "__main__":
    Demo()
    arcade.run()

Особенности платформ

Путь загрузки в pyglet может отличаться в зависимости от ОС. В частности, Windows может использовать Win32DirectWriteFont или GDIPlusFont, тогда как в примере выше использовался Linux. В одном случае тот же шрифт и код корректно отображались в Linux, но не показывались в Windows, и простая замена шрифта решила проблему.

Почему это важно

Текст — ключевой элемент интерфейса. Неверный идентификатор приводит к непредсказуемым запасным шрифтам, разношерстному стилю и трудноуловимым багам. Использование внутреннего имени гарантирует, что рендер выберет нужное начертание и даст стабильный результат между запусками. При отладке на разных операционных системах понимание механизма выбора заметно сокращает цикл проверки.

Выводы

Загружайте .ttf через Arcade, но выбирайте шрифт по его внутреннему имени, которое сообщает FreeTypeFace в pyglet, и передавайте это значение в font_name. Если на конкретной платформе шрифт всё же не отображается как ожидается, учитывайте возможные различия в бэкенде шрифтов ОС и попробуйте альтернативный файл шрифта.

Статья основана на вопросе на StackOverflow от JediKnightBoB и ответе furas.