Merge pull request #7806 from Gymnasiast/feature/rtl-rendering

Fix RTL text rendering for Linux / macOS
This commit is contained in:
Ted John 2018-10-16 22:26:45 +01:00 committed by GitHub
commit 211bd84cc3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 81 additions and 26 deletions

View File

@ -23,30 +23,30 @@
// clang-format off
const language_descriptor LanguagesDescriptors[LANGUAGE_COUNT] =
{
{ "", "", "", FAMILY_OPENRCT2_SPRITE, RCT2_LANGUAGE_ID_ENGLISH_UK }, // LANGUAGE_UNDEFINED
{ "ar-EG", "Arabic (experimental)", "Arabic (experimental)", FAMILY(&TTFFamilySansSerif), RCT2_LANGUAGE_ID_ENGLISH_UK }, // LANGUAGE_ARABIC
{ "ca-ES", "Catalan", u8"Català", FAMILY_OPENRCT2_SPRITE, RCT2_LANGUAGE_ID_SPANISH }, // LANGUAGE_CATALAN
{ "zh-CN", "Chinese (Simplified)", "Chinese (Simplified)", FAMILY(&TTFFamilyChineseSimplified), RCT2_LANGUAGE_ID_CHINESE_SIMPLIFIED }, // LANGUAGE_CHINESE_SIMPLIFIED
{ "zh-TW", "Chinese (Traditional)", "Chinese (Traditional)", FAMILY(&TTFFamilyChineseTraditional), RCT2_LANGUAGE_ID_CHINESE_TRADITIONAL }, // LANGUAGE_CHINESE_TRADITIONAL
{ "cs-CZ", "Czech", "Czech", FAMILY(&TTFFamilySansSerif), RCT2_LANGUAGE_ID_ENGLISH_UK }, // LANGUAGE_CZECH
{ "da-DK", "Danish", "Dansk", FAMILY_OPENRCT2_SPRITE, RCT2_LANGUAGE_ID_ENGLISH_UK }, // LANGUAGE_DANISH
{ "de-DE", "German", "Deutsch", FAMILY_OPENRCT2_SPRITE, RCT2_LANGUAGE_ID_GERMAN }, // LANGUAGE_GERMAN
{ "en-GB", "English (UK)", "English (UK)", FAMILY_OPENRCT2_SPRITE, RCT2_LANGUAGE_ID_ENGLISH_UK }, // LANGUAGE_ENGLISH_UK
{ "en-US", "English (US)", "English (US)", FAMILY_OPENRCT2_SPRITE, RCT2_LANGUAGE_ID_ENGLISH_US }, // LANGUAGE_ENGLISH_US
{ "es-ES", "Spanish", u8"Español", FAMILY_OPENRCT2_SPRITE, RCT2_LANGUAGE_ID_SPANISH }, // LANGUAGE_SPANISH
{ "fr-FR", "French", u8"Français", FAMILY_OPENRCT2_SPRITE, RCT2_LANGUAGE_ID_FRENCH }, // LANGUAGE_FRENCH
{ "it-IT", "Italian", "Italiano", FAMILY_OPENRCT2_SPRITE, RCT2_LANGUAGE_ID_ITALIAN }, // LANGUAGE_ITALIAN
{ "ja-JP", "Japanese", "Japanese", FAMILY(&TTFFamilyJapanese), RCT2_LANGUAGE_ID_ENGLISH_UK }, // LANGUAGE_JAPANESE
{ "ko-KR", "Korean", "Korean", FAMILY(&TTFFamilyKorean), RCT2_LANGUAGE_ID_KOREAN }, // LANGUAGE_KOREAN
{ "hu-HU", "Hungarian", "Magyar", FAMILY_OPENRCT2_SPRITE, RCT2_LANGUAGE_ID_ENGLISH_UK }, // LANGUAGE_HUNGARIAN
{ "nl-NL", "Dutch", "Nederlands", FAMILY_OPENRCT2_SPRITE, RCT2_LANGUAGE_ID_DUTCH }, // LANGUAGE_DUTCH
{ "nb-NO", "Norwegian", "Norsk", FAMILY_OPENRCT2_SPRITE, RCT2_LANGUAGE_ID_ENGLISH_UK }, // LANGUAGE_NORWEGIAN
{ "pl-PL", "Polish", "Polski", FAMILY_OPENRCT2_SPRITE, RCT2_LANGUAGE_ID_ENGLISH_UK }, // LANGUAGE_POLISH
{ "pt-BR", "Portuguese (BR)", u8"Português (BR)", FAMILY_OPENRCT2_SPRITE, RCT2_LANGUAGE_ID_PORTUGUESE }, // LANGUAGE_PORTUGUESE_BR
{ "ru-RU", "Russian", u8"Русский", FAMILY_OPENRCT2_SPRITE, RCT2_LANGUAGE_ID_ENGLISH_UK }, // LANGUAGE_RUSSIAN
{ "fi-FI", "Finnish", "Suomi", FAMILY_OPENRCT2_SPRITE, RCT2_LANGUAGE_ID_ENGLISH_UK }, // LANGUAGE_FINNISH
{ "sv-SE", "Swedish", "Svenska", FAMILY_OPENRCT2_SPRITE, RCT2_LANGUAGE_ID_SWEDISH }, // LANGUAGE_SWEDISH
{ "tr-TR", "Turkish", "Türkçe", FAMILY_OPENRCT2_SPRITE, RCT2_LANGUAGE_ID_ENGLISH_UK }, // LANGUAGE_TURKISH
{ "", "", "", FAMILY_OPENRCT2_SPRITE, false }, // LANGUAGE_UNDEFINED
{ "ar-EG", "Arabic (experimental)", "Arabic (experimental)", FAMILY(&TTFFamilySansSerif), true }, // LANGUAGE_ARABIC
{ "ca-ES", "Catalan", u8"Català", FAMILY_OPENRCT2_SPRITE, false }, // LANGUAGE_CATALAN
{ "zh-CN", "Chinese (Simplified)", "Chinese (Simplified)", FAMILY(&TTFFamilyChineseSimplified), false }, // LANGUAGE_CHINESE_SIMPLIFIED
{ "zh-TW", "Chinese (Traditional)", "Chinese (Traditional)", FAMILY(&TTFFamilyChineseTraditional), false }, // LANGUAGE_CHINESE_TRADITIONAL
{ "cs-CZ", "Czech", "Czech", FAMILY(&TTFFamilySansSerif), false }, // LANGUAGE_CZECH
{ "da-DK", "Danish", "Dansk", FAMILY_OPENRCT2_SPRITE, false }, // LANGUAGE_DANISH
{ "de-DE", "German", "Deutsch", FAMILY_OPENRCT2_SPRITE, false }, // LANGUAGE_GERMAN
{ "en-GB", "English (UK)", "English (UK)", FAMILY_OPENRCT2_SPRITE, false }, // LANGUAGE_ENGLISH_UK
{ "en-US", "English (US)", "English (US)", FAMILY_OPENRCT2_SPRITE, false }, // LANGUAGE_ENGLISH_US
{ "es-ES", "Spanish", u8"Español", FAMILY_OPENRCT2_SPRITE, false }, // LANGUAGE_SPANISH
{ "fr-FR", "French", u8"Français", FAMILY_OPENRCT2_SPRITE, false }, // LANGUAGE_FRENCH
{ "it-IT", "Italian", "Italiano", FAMILY_OPENRCT2_SPRITE, false }, // LANGUAGE_ITALIAN
{ "ja-JP", "Japanese", "Japanese", FAMILY(&TTFFamilyJapanese), false }, // LANGUAGE_JAPANESE
{ "ko-KR", "Korean", "Korean", FAMILY(&TTFFamilyKorean), false }, // LANGUAGE_KOREAN
{ "hu-HU", "Hungarian", "Magyar", FAMILY_OPENRCT2_SPRITE, false }, // LANGUAGE_HUNGARIAN
{ "nl-NL", "Dutch", "Nederlands", FAMILY_OPENRCT2_SPRITE, false }, // LANGUAGE_DUTCH
{ "nb-NO", "Norwegian", "Norsk", FAMILY_OPENRCT2_SPRITE, false }, // LANGUAGE_NORWEGIAN
{ "pl-PL", "Polish", "Polski", FAMILY_OPENRCT2_SPRITE, false }, // LANGUAGE_POLISH
{ "pt-BR", "Portuguese (BR)", u8"Português (BR)", FAMILY_OPENRCT2_SPRITE, false }, // LANGUAGE_PORTUGUESE_BR
{ "ru-RU", "Russian", u8"Русский", FAMILY_OPENRCT2_SPRITE, false }, // LANGUAGE_RUSSIAN
{ "fi-FI", "Finnish", "Suomi", FAMILY_OPENRCT2_SPRITE, false }, // LANGUAGE_FINNISH
{ "sv-SE", "Swedish", "Svenska", FAMILY_OPENRCT2_SPRITE, false }, // LANGUAGE_SWEDISH
{ "tr-TR", "Turkish", "Türkçe", FAMILY_OPENRCT2_SPRITE, false }, // LANGUAGE_TURKISH
};
// clang-format on

View File

@ -79,7 +79,7 @@ struct language_descriptor
#else
void* font_family;
#endif
RCT2LanguageId rct2_original_id;
bool isRtl;
};
extern const language_descriptor LanguagesDescriptors[LANGUAGE_COUNT];

View File

@ -15,11 +15,20 @@
#include "../core/String.hpp"
#include "../core/StringBuilder.hpp"
#include "../core/StringReader.hpp"
#include "Language.h"
#include "Localisation.h"
#include <algorithm>
#include <string>
#include <vector>
#ifndef _WIN32
# include <unicode/ubidi.h>
# include <unicode/unistr.h>
# include <unicode/ushape.h>
# include <unicode/ustring.h>
# include <unicode/utf.h>
# include <unicode/utypes.h>
#endif
// Don't try to load more than language files that exceed 64 MiB
constexpr uint64_t MAX_LANGUAGE_SIZE = 64 * 1024 * 1024;
@ -564,7 +573,17 @@ private:
}
}
auto s = std::string(sb.GetBuffer(), sb.GetLength());
std::string s;
if (LanguagesDescriptors[_id].isRtl)
{
auto ts = std::string(sb.GetBuffer(), sb.GetLength());
s = FixRTL(ts);
}
else
{
s = std::string(sb.GetBuffer(), sb.GetLength());
}
if (_currentGroup.empty())
{
// Make sure the list is big enough to contain this string id
@ -627,6 +646,37 @@ private:
return true;
}
std::string FixRTL(std::string& input)
{
#ifdef _WIN32
return input;
#else
UErrorCode err = (UErrorCode)0;
// Force a hard left-to-right at the beginning (will mess up mixed strings' word order otherwise)
std::string text2 = std::string(u8"\xE2\x80\xAA") + input;
icu::UnicodeString ustr = icu::UnicodeString::fromUTF8(icu::StringPiece(text2));
int32_t length = ustr.length();
icu::UnicodeString reordered;
icu::UnicodeString shaped;
UBiDi* bidi = ubidi_openSized(length, 0, &err);
// UBIDI_DEFAULT_LTR preserves formatting codes.
ubidi_setPara(bidi, ustr.getBuffer(), length, UBIDI_DEFAULT_LTR, nullptr, &err);
ubidi_writeReordered(bidi, reordered.getBuffer(length), length, UBIDI_DO_MIRRORING | UBIDI_REMOVE_BIDI_CONTROLS, &err);
ubidi_close(bidi);
reordered.releaseBuffer(length);
u_shapeArabic(
reordered.getBuffer(), length, shaped.getBuffer(length), length,
U_SHAPE_LETTERS_SHAPE | U_SHAPE_LENGTH_FIXED_SPACES_NEAR | U_SHAPE_TEXT_DIRECTION_VISUAL_LTR, &err);
shaped.releaseBuffer(length);
std::string cppstring;
shaped.toUTF8String(cppstring);
return cppstring;
#endif
}
};
namespace LanguagePackFactory

View File

@ -9,10 +9,15 @@
#include "openrct2/localisation/LanguagePack.h"
#include "openrct2/localisation/Language.h"
#include "openrct2/localisation/StringIds.h"
#include <gtest/gtest.h>
#ifndef _WIN32
const language_descriptor LanguagesDescriptors[] = {};
#endif
class LanguagePackTest : public testing::Test
{
protected: