Merge pull request #9185 from Robyt3/Client-Font-Error

Improve error handling on invalid fonts and font index
This commit is contained in:
Dennis Felsing 2024-11-01 07:50:48 +00:00 committed by GitHub
commit 0f91b1287d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 121 additions and 45 deletions

View file

@ -615,37 +615,65 @@ public:
void AddFace(FT_Face Face)
{
m_vFtFaces.push_back(Face);
if(!m_DefaultFace)
m_DefaultFace = Face;
}
void SetDefaultFaceByName(const char *pFamilyName)
bool SetDefaultFaceByName(const char *pFamilyName)
{
m_DefaultFace = GetFaceByName(pFamilyName);
if(!m_DefaultFace)
{
if(!m_vFtFaces.empty())
{
m_DefaultFace = m_vFtFaces.front();
}
log_error("textrender", "The default font face '%s' could not be found", pFamilyName);
return false;
}
return true;
}
void SetIconFaceByName(const char *pFamilyName)
bool SetIconFaceByName(const char *pFamilyName)
{
m_IconFace = GetFaceByName(pFamilyName);
if(!m_IconFace)
{
log_error("textrender", "The icon font face '%s' could not be found", pFamilyName);
return false;
}
return true;
}
void AddFallbackFaceByName(const char *pFamilyName)
bool AddFallbackFaceByName(const char *pFamilyName)
{
FT_Face Face = GetFaceByName(pFamilyName);
if(Face != nullptr && std::find(m_vFallbackFaces.begin(), m_vFallbackFaces.end(), Face) == m_vFallbackFaces.end())
if(!Face)
{
m_vFallbackFaces.push_back(Face);
log_error("textrender", "The fallback font face '%s' could not be found", pFamilyName);
return false;
}
if(std::find(m_vFallbackFaces.begin(), m_vFallbackFaces.end(), Face) != m_vFallbackFaces.end())
{
log_warn("textrender", "The fallback font face '%s' was specified multiple times", pFamilyName);
return true;
}
m_vFallbackFaces.push_back(Face);
return true;
}
void SetVariantFaceByName(const char *pFamilyName)
bool SetVariantFaceByName(const char *pFamilyName)
{
FT_Face Face = GetFaceByName(pFamilyName);
if(m_VariantFace != Face)
{
m_VariantFace = Face;
Clear(); // rebuild atlas after changing variant font
if(!Face && pFamilyName != nullptr)
{
log_error("textrender", "The variant font face '%s' could not be found", pFamilyName);
return false;
}
}
return true;
}
void SetFontPreset(EFontPreset FontPreset)
@ -1002,9 +1030,7 @@ class CTextRender : public IEngineTextRender
FT_Error CollectionLoadError = FT_New_Memory_Face(m_FTLibrary, pFontData, FontDataSize, -1, &FtFace);
if(CollectionLoadError)
{
char aBuf[256];
str_format(aBuf, sizeof(aBuf), "Failed to load font file '%s': %s", pFontName, FT_Error_String(CollectionLoadError));
Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "textrender", aBuf);
log_error("textrender", "Failed to load font file '%s': %s", pFontName, FT_Error_String(CollectionLoadError));
return false;
}
@ -1017,26 +1043,20 @@ class CTextRender : public IEngineTextRender
FT_Error FaceLoadError = FT_New_Memory_Face(m_FTLibrary, pFontData, FontDataSize, FaceIndex, &FtFace);
if(FaceLoadError)
{
char aBuf[256];
str_format(aBuf, sizeof(aBuf), "Failed to load font face %ld from font file '%s': %s", FaceIndex, pFontName, FT_Error_String(FaceLoadError));
Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "textrender", aBuf);
log_error("textrender", "Failed to load font face %ld from font file '%s': %s", FaceIndex, pFontName, FT_Error_String(FaceLoadError));
FT_Done_Face(FtFace);
continue;
}
m_pGlyphMap->AddFace(FtFace);
char aBuf[256];
str_format(aBuf, sizeof(aBuf), "Loaded font face %ld '%s %s' from font file '%s'", FaceIndex, FtFace->family_name, FtFace->style_name, pFontName);
Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "textrender", aBuf);
log_debug("textrender", "Loaded font face %ld '%s %s' from font file '%s'", FaceIndex, FtFace->family_name, FtFace->style_name, pFontName);
LoadedAny = true;
}
if(!LoadedAny)
{
char aBuf[256];
str_format(aBuf, sizeof(aBuf), "Failed to load font file '%s': no font faces could be loaded", pFontName);
Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "textrender", aBuf);
log_error("textrender", "Failed to load font file '%s': no font faces could be loaded", pFontName);
return false;
}
@ -1083,9 +1103,7 @@ public:
{
int LMajor, LMinor, LPatch;
FT_Library_Version(m_FTLibrary, &LMajor, &LMinor, &LPatch);
char aFreetypeVersion[128];
str_format(aFreetypeVersion, sizeof(aFreetypeVersion), "Freetype version %d.%d.%d (compiled = %d.%d.%d)", LMajor, LMinor, LPatch, FREETYPE_MAJOR, FREETYPE_MINOR, FREETYPE_PATCH);
Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "textrender", aFreetypeVersion);
log_info("textrender", "Freetype version %d.%d.%d (compiled = %d.%d.%d)", LMajor, LMinor, LPatch, FREETYPE_MAJOR, FREETYPE_MINOR, FREETYPE_PATCH);
}
m_FirstFreeTextContainerIndex = -1;
@ -1142,7 +1160,7 @@ public:
m_pStorage = nullptr;
}
void LoadFonts() override
bool LoadFonts() override
{
// read file data into buffer
const char *pFilename = "fonts/index.json";
@ -1150,10 +1168,8 @@ public:
unsigned JsonFileSize;
if(!Storage()->ReadFile(pFilename, IStorage::TYPE_ALL, &pFileData, &JsonFileSize))
{
char aBuf[256];
str_format(aBuf, sizeof(aBuf), "Failed to open/read font index file '%s'", pFilename);
Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "textrender", aBuf);
return;
log_error("textrender", "Failed to open/read font index file '%s'", pFilename);
return false;
}
// parse json data
@ -1163,11 +1179,16 @@ public:
free(pFileData);
if(pJsonData == nullptr)
{
char aBuf[512];
str_format(aBuf, sizeof(aBuf), "Failed to parse font index file '%s': %s", pFilename, aError);
Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "textrender", aBuf);
return;
log_error("textrender", "Failed to parse font index file '%s': %s", pFilename, aError);
return false;
}
if(pJsonData->type != json_object)
{
log_error("textrender", "Font index malformed: root must be an object", pFilename, aError);
return false;
}
bool Success = true;
// extract font file definitions
const json_value &FontFiles = (*pJsonData)["font files"];
@ -1176,7 +1197,11 @@ public:
for(unsigned FontFileIndex = 0; FontFileIndex < FontFiles.u.array.length; ++FontFileIndex)
{
if(FontFiles[FontFileIndex].type != json_string)
{
log_error("textrender", "Font index malformed: 'font files' must be an array of strings (error at index %d)", FontFileIndex);
Success = false;
continue;
}
char aFontName[IO_MAX_PATH_LENGTH];
str_format(aFontName, sizeof(aFontName), "fonts/%s", FontFiles[FontFileIndex].u.string.ptr);
@ -1193,32 +1218,60 @@ public:
free(pFontData);
}
}
else
{
log_error("textrender", "Failed to open/read font file '%s'", aFontName);
Success = false;
}
}
}
else
{
log_error("textrender", "Font index malformed: 'font files' must be an array");
Success = false;
}
// extract default family name
const json_value &DefaultFace = (*pJsonData)["default"];
if(DefaultFace.type == json_string)
{
m_pGlyphMap->SetDefaultFaceByName(DefaultFace.u.string.ptr);
if(!m_pGlyphMap->SetDefaultFaceByName(DefaultFace.u.string.ptr))
{
Success = false;
}
}
else
{
log_error("textrender", "Font index malformed: 'default' must be a string");
Success = false;
}
// extract language variant family names
const json_value &Variants = (*pJsonData)["language variants"];
if(Variants.type == json_object)
{
m_vVariants.resize(Variants.u.object.length);
m_vVariants.reserve(Variants.u.object.length);
for(size_t i = 0; i < Variants.u.object.length; ++i)
{
str_format(m_vVariants[i].m_aLanguageFile, sizeof(m_vVariants[i].m_aLanguageFile), "languages/%s.txt", Variants.u.object.values[i].name);
const json_value *pFamilyName = Variants.u.object.values[i].value;
if(pFamilyName->type == json_string)
str_copy(m_vVariants[i].m_aFamilyName, pFamilyName->u.string.ptr);
else
m_vVariants[i].m_aFamilyName[0] = '\0';
if(pFamilyName->type != json_string)
{
log_error("textrender", "Font index malformed: 'language variants' entries must have string values (error on entry '%s')", Variants.u.object.values[i].name);
Success = false;
continue;
}
SFontLanguageVariant Variant;
str_format(Variant.m_aLanguageFile, sizeof(Variant.m_aLanguageFile), "languages/%s.txt", Variants.u.object.values[i].name);
str_copy(Variant.m_aFamilyName, pFamilyName->u.string.ptr);
m_vVariants.emplace_back(Variant);
}
}
else
{
log_error("textrender", "Font index malformed: 'language variants' must be an array");
Success = false;
}
// extract fallback family names
const json_value &FallbackFaces = (*pJsonData)["fallbacks"];
@ -1226,21 +1279,41 @@ public:
{
for(unsigned i = 0; i < FallbackFaces.u.array.length; ++i)
{
if(FallbackFaces[i].type == json_string)
if(FallbackFaces[i].type != json_string)
{
m_pGlyphMap->AddFallbackFaceByName(FallbackFaces[i].u.string.ptr);
log_error("textrender", "Font index malformed: 'fallbacks' must be an array of strings (error at index %d)", i);
Success = false;
continue;
}
if(!m_pGlyphMap->AddFallbackFaceByName(FallbackFaces[i].u.string.ptr))
{
Success = false;
}
}
}
else
{
log_error("textrender", "Font index malformed: 'fallbacks' must be an array");
Success = false;
}
// extract icon font family name
const json_value &IconFace = (*pJsonData)["icon"];
if(IconFace.type == json_string)
{
m_pGlyphMap->SetIconFaceByName(IconFace.u.string.ptr);
if(!m_pGlyphMap->SetIconFaceByName(IconFace.u.string.ptr))
{
Success = false;
}
}
else
{
log_error("textrender", "Font index malformed: 'icon' must be a string");
Success = false;
}
json_value_free(pJsonData);
return Success;
}
void SetFontPreset(EFontPreset FontPreset) override

View file

@ -316,7 +316,7 @@ public:
virtual void MoveCursor(CTextCursor *pCursor, float x, float y) const = 0;
virtual void SetCursorPosition(CTextCursor *pCursor, float x, float y) const = 0;
virtual void LoadFonts() = 0;
virtual bool LoadFonts() = 0;
virtual void SetFontPreset(EFontPreset FontPreset) = 0;
virtual void SetFontLanguageVariant(const char *pLanguageFile) = 0;

View file

@ -328,7 +328,10 @@ void CGameClient::OnInit()
for(int i = 0; i < OLD_NUM_NETOBJTYPES; i++)
Client()->SnapSetStaticsize7(i, m_NetObjHandler7.GetObjSize(i));
TextRender()->LoadFonts();
if(!TextRender()->LoadFonts())
{
Client()->AddWarning(SWarning(Localize("Some fonts could not be loaded. Check the local console for details.")));
}
TextRender()->SetFontLanguageVariant(g_Config.m_ClLanguagefile);
// update and swap after font loading, they are quite huge