Use Noto fonts for Asian languages (#36514)

ui: use Noto fonts for Asian languages

Co-authored-by: Adeeb Shihadeh <adeebshihadeh@gmail.com>
This commit is contained in:
eFini
2026-08-08 04:30:58 +08:00
committed by GitHub
parent b77da00697
commit 66b3590b63
7 changed files with 52 additions and 10 deletions
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:80135e7ab71496c3b744047ef9100f5d24edaa275d248a3e0488bc769e453baa
size 240500
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e86fb33e127558b845aec3016d46c36118811a1e30fd9dacc374840ad3001221
size 109356
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:71933d4233b691fb499524cbf9b3d74ccdf6465c81791687e661667aefa8e660
size 196068
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:292f8ad096fd0a47e4016bd3607cb4edcd3b1f97634f67a115c0b0973ba5002f
size 245884
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:8656eb9e4e182333e58c59cd07ae0dd82c913d31c29b0d2726887ffbbef137a2
size 26536
+34 -6
View File
@@ -19,7 +19,7 @@ from typing import NamedTuple
from importlib.resources import as_file, files
from openpilot.common.swaglog import cloudlog
from openpilot.common.hardware import HARDWARE, PC
from openpilot.system.ui.lib.multilang import TRANSLATIONS_DIR, UNIFONT_LANGUAGES, multilang
from openpilot.system.ui.lib.multilang import FONT_FALLBACK_LANGUAGES, TRANSLATIONS_DIR, multilang
from openpilot.common.realtime import Ratekeeper
_DEFAULT_FPS = int(os.getenv("FPS", {'tizi': 20}.get(HARDWARE.get_device_type(), 60)))
@@ -93,6 +93,13 @@ FONT_SCALE = 1.242 if BIG_UI else 1.16
ASSETS_DIR = files("openpilot.selfdrive").joinpath("assets")
FONT_DIR = ASSETS_DIR.joinpath("fonts")
EXTRA_FONT_CHARS = "–‑✓×°§•X⚙✕◀▶✔⌫⇧␣○●↳çêüñ–‑✓×°§•€£¥"
NOTO_FONTS = {
"ja": "NotoSansCJKjp-Regular.otf",
"ko": "NotoSansCJKkr-Regular.otf",
"th": "NotoSansThai-Regular.ttf",
"zh-CHS": "NotoSansCJKsc-Regular.otf",
"zh-CHT": "NotoSansCJKtc-Regular.otf",
}
class FontWeight(StrEnum):
@@ -109,9 +116,9 @@ class FontWeight(StrEnum):
def font_fallback(font: rl.Font) -> rl.Font:
"""Fall back to unifont for languages that require it."""
if multilang.requires_unifont():
return gui_app.font(FontWeight.UNIFONT)
"""Use a Noto fallback for languages not covered by Inter."""
if multilang.requires_font_fallback():
return gui_app.fallback_font()
return font
@@ -199,6 +206,7 @@ class GuiApplication:
self._set_log_callback()
self._fonts: dict[FontWeight, rl.Font] = {}
self._fallback_fonts: dict[str, rl.Font] = {}
self._width = width if width is not None else GuiApplication._default_width()
self._height = height if height is not None else GuiApplication._default_height()
@@ -554,6 +562,9 @@ class GuiApplication:
for font in self._fonts.values():
rl.unload_font(font)
self._fonts = {}
for font in self._fallback_fonts.values():
rl.unload_font(font)
self._fallback_fonts = {}
if self._render_texture is not None:
rl.unload_render_texture(self._render_texture)
@@ -676,6 +687,21 @@ class GuiApplication:
def font(self, font_weight: FontWeight = FontWeight.NORMAL) -> rl.Font:
return self._fonts[font_weight]
def fallback_font(self) -> rl.Font:
language = multilang.language
if language not in self._fallback_fonts:
chars = set(map(chr, range(32, 127))) | set(EXTRA_FONT_CHARS)
chars.update(TRANSLATIONS_DIR.joinpath(f"app_{language}.po").read_text(encoding="utf-8"))
codepoints = sorted(map(ord, chars))
codepoint_buffer = rl.ffi.new("int[]", codepoints)
with as_file(FONT_DIR) as fspath:
font = rl.load_font_ex((fspath / NOTO_FONTS[language]).as_posix(), 48,
rl.ffi.cast("int *", codepoint_buffer), len(codepoints))
rl.gen_texture_mipmaps(font.texture)
rl.set_texture_filter(font.texture, rl.TextureFilter.TEXTURE_FILTER_TRILINEAR)
self._fallback_fonts[language] = font
return self._fallback_fonts[language]
@property
def width(self):
return self._width
@@ -689,8 +715,8 @@ class GuiApplication:
unifont_chars = set(base_chars)
for language, code in multilang.languages.items():
unifont_chars.update(language)
chars = set(TRANSLATIONS_DIR.joinpath(f"app_{code}.po").read_text(encoding="utf-8"))
(unifont_chars if code in UNIFONT_LANGUAGES else base_chars).update(chars)
if code not in FONT_FALLBACK_LANGUAGES:
base_chars.update(TRANSLATIONS_DIR.joinpath(f"app_{code}.po").read_text(encoding="utf-8"))
for font_weight_file in FontWeight:
with as_file(FONT_DIR) as fspath:
@@ -703,6 +729,8 @@ class GuiApplication:
rl.gen_texture_mipmaps(font.texture)
rl.set_texture_filter(font.texture, rl.TextureFilter.TEXTURE_FILTER_TRILINEAR)
self._fonts[font_weight_file] = font
if multilang.requires_font_fallback():
self.fallback_font()
rl.gui_set_font(self._fonts[FontWeight.NORMAL])
def _set_styles(self):
+3 -4
View File
@@ -19,7 +19,7 @@ UI_DIR = files("openpilot.selfdrive.ui")
TRANSLATIONS_DIR = UI_DIR.joinpath("translations")
LANGUAGES_FILE = TRANSLATIONS_DIR.joinpath("languages.json")
UNIFONT_LANGUAGES = [
FONT_FALLBACK_LANGUAGES = [
"th",
"zh-CHT",
"zh-CHS",
@@ -164,9 +164,8 @@ class Multilang:
def language(self) -> str:
return self._language
def requires_unifont(self) -> bool:
"""Certain languages require unifont to render their glyphs."""
return self._language in UNIFONT_LANGUAGES
def requires_font_fallback(self) -> bool:
return self._language in FONT_FALLBACK_LANGUAGES
def setup(self):
try: