From 1439187e546f82bb22c03cfcaa33f045c26cd429 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 8 Nov 2026 14:07:20 +0100 Subject: [PATCH] Offer Arabic and Persian wherever a text drawer can shape them They were only listed on Windows and Android, from before the Cocoa and SDL3_ttf drawers existed, so Mac, iOS and Linux didn't have them. The SDL drawer now takes the paragraph direction from the RTL embedding TextDrawer adds, and strips the marks before FriBidi sees them. Otherwise the marks count as a second level, and every Arabic line would go through FriBidi's reordering and shaping instead of SDL3_ttf's. Co-Authored-By: Claude Opus 5.5 --- Common/Render/Text/draw_text_sdl.cpp | 10 +++++++++- UI/MiscScreens.cpp | 5 ++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/Common/Render/Text/draw_text_sdl.cpp b/Common/Render/Text/draw_text_sdl.cpp index ab93190da6..02a08072d6 100644 --- a/Common/Render/Text/draw_text_sdl.cpp +++ b/Common/Render/Text/draw_text_sdl.cpp @@ -58,6 +58,15 @@ static bool ReorderBidi(std::string_view str, std::string *out) { result.push_back('\n'); } std::string_view line = lines[i]; + FriBidiParType baseDir = FRIBIDI_PAR_ON; + // TextDrawer::ApplyBaseDirection wraps RTL lines in RLE ... PDF. Take the direction from that instead, + // so the marks don't count as a second level and send pure Arabic lines down the reordering path. + const std::string_view rle = "\xE2\x80\xAB"; + const std::string_view pdf = "\xE2\x80\xAC"; + if (line.size() >= rle.size() + pdf.size() && startsWith(line, rle) && endsWith(line, pdf)) { + line = line.substr(rle.size(), line.size() - rle.size() - pdf.size()); + baseDir = FRIBIDI_PAR_RTL; + } logical.resize(line.size() + 1); FriBidiStrIndex len = fribidi_charset_to_unicode(FRIBIDI_CHAR_SET_UTF8, line.data(), (FriBidiStrIndex)line.size(), logical.data()); if (len <= 0) { @@ -66,7 +75,6 @@ static bool ReorderBidi(std::string_view str, std::string *out) { } visual.resize(len + 1); levels.resize(len); - FriBidiParType baseDir = FRIBIDI_PAR_ON; if (!fribidi_log2vis(logical.data(), len, &baseDir, visual.data(), nullptr, nullptr, levels.data())) { return false; } diff --git a/UI/MiscScreens.cpp b/UI/MiscScreens.cpp index c5e981a65f..941bd0c755 100644 --- a/UI/MiscScreens.cpp +++ b/UI/MiscScreens.cpp @@ -304,9 +304,8 @@ NewLanguageScreen::NewLanguageScreen(std::string_view title) : ListPopupScreen(t continue; } - // We only support Arabic on platforms where we have support for the native text rendering - // APIs, as proper Arabic support is way too difficult to implement ourselves. -#if !(PPSSPP_PLATFORM(WINDOWS) || PPSSPP_PLATFORM(ANDROID)) + // Arabic script needs a text drawer that does shaping (see TextDrawer::Create), the atlas font can't. +#if defined(__LIBRETRO__) || !(PPSSPP_PLATFORM(WINDOWS) || PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(MAC) || PPSSPP_PLATFORM(IOS) || defined(USE_SDL3_TTF)) if (tempLangs[i].name.find("ar_AE") != std::string::npos) { continue; }