diff --git a/src/g_cvars.cpp b/src/g_cvars.cpp index 573fe04c9..ad3daa769 100644 --- a/src/g_cvars.cpp +++ b/src/g_cvars.cpp @@ -140,20 +140,7 @@ bool CheckFontComplete(FFont *font) { // Also check if the SmallFont contains all characters this language needs. // If not, switch back to the original one. - const uint8_t* checkstr = (const uint8_t*)GStrings["REQUIRED_CHARACTERS"]; - bool incomplete = false; - - if (!checkstr) return true; - while (int c = GetCharFromString(checkstr)) - { - bool redirected; - int cc = font->GetCharCode(c, true); - if (c != cc && (c != 0x1e9e || cc != 0xdf)) - { - return false; - } - } - return true; + return font->CanPrint(GStrings["REQUIRED_CHARACTERS"]); } void UpdateGenericUI(bool cvar) diff --git a/src/g_statusbar/shared_sbar.cpp b/src/g_statusbar/shared_sbar.cpp index 30e8b064b..1ec2be607 100644 --- a/src/g_statusbar/shared_sbar.cpp +++ b/src/g_statusbar/shared_sbar.cpp @@ -597,6 +597,7 @@ void DBaseStatusBar::DoDrawAutomapHUD(int crdefault, int highlight) { auto scale = GetUIScale(hud_scale); auto font = generic_ui ? NewSmallFont : SmallFont; + auto font2 = font; auto vwidth = screen->GetWidth() / scale; auto vheight = screen->GetHeight() / scale; auto fheight = font->GetHeight(); @@ -606,6 +607,11 @@ void DBaseStatusBar::DoDrawAutomapHUD(int crdefault, int highlight) int textdist = 4; int zerowidth = font->GetCharWidth('0'); + if (!generic_ui) + { + if (!font->CanPrint(GStrings("AM_MONSTERS")) || !font->CanPrint(GStrings("AM_SECRETS")) || !font->CanPrint(GStrings("AM_ITEMS"))) font2 = OriginalSmallFont; + } + if (am_showtime) { sec = Tics2Seconds(primaryLevel->time); @@ -629,14 +635,14 @@ void DBaseStatusBar::DoDrawAutomapHUD(int crdefault, int highlight) if (am_showmonsters) { textbuffer.Format("%s\34%c %d/%d", GStrings("AM_MONSTERS"), crdefault + 65, primaryLevel->killed_monsters, primaryLevel->total_monsters); - screen->DrawText(font, highlight, textdist, y, textbuffer, DTA_KeepRatio, true, DTA_VirtualWidth, vwidth, DTA_VirtualHeight, vheight, TAG_DONE); + screen->DrawText(font2, highlight, textdist, y, textbuffer, DTA_KeepRatio, true, DTA_VirtualWidth, vwidth, DTA_VirtualHeight, vheight, TAG_DONE); y += fheight; } if (am_showsecrets) { textbuffer.Format("%s\34%c %d/%d", GStrings("AM_SECRETS"), crdefault + 65, primaryLevel->found_secrets, primaryLevel->total_secrets); - screen->DrawText(font, highlight, textdist, y, textbuffer, DTA_KeepRatio, true, DTA_VirtualWidth, vwidth, DTA_VirtualHeight, vheight, TAG_DONE); + screen->DrawText(font2, highlight, textdist, y, textbuffer, DTA_KeepRatio, true, DTA_VirtualWidth, vwidth, DTA_VirtualHeight, vheight, TAG_DONE); y += fheight; } @@ -644,7 +650,7 @@ void DBaseStatusBar::DoDrawAutomapHUD(int crdefault, int highlight) if (am_showitems) { textbuffer.Format("%s\34%c %d/%d", GStrings("AM_ITEMS"), crdefault + 65, primaryLevel->found_items, primaryLevel->total_items); - screen->DrawText(font, highlight, textdist, y, textbuffer, DTA_KeepRatio, true, DTA_VirtualWidth, vwidth, DTA_VirtualHeight, vheight, TAG_DONE); + screen->DrawText(font2, highlight, textdist, y, textbuffer, DTA_KeepRatio, true, DTA_VirtualWidth, vwidth, DTA_VirtualHeight, vheight, TAG_DONE); y += fheight; } diff --git a/src/gamedata/fonts/font.cpp b/src/gamedata/fonts/font.cpp index 7d09f37cb..7a74daf75 100644 --- a/src/gamedata/fonts/font.cpp +++ b/src/gamedata/fonts/font.cpp @@ -482,6 +482,51 @@ FFont::~FFont () } } +//========================================================================== +// +// FFont :: CheckCase +// +//========================================================================== + +void FFont::CheckCase() +{ + int lowercount = 0, uppercount = 0; + for (unsigned i = 0; i < Chars.Size(); i++) + { + unsigned chr = i + FirstChar; + if (lowerforupper[chr] == chr && upperforlower[chr] == chr) + { + continue; // not a letter; + } + if (myislower(chr)) + { + if (Chars[i].TranslatedPic != nullptr) lowercount++; + } + else + { + if (Chars[i].TranslatedPic != nullptr) uppercount++; + } + } + if (lowercount == 0) return; // This is an uppercase-only font and we are done. + + // The ß needs special treatment because it is far more likely to be supplied lowercase only, even in an uppercase font. + if (Chars[0xdf - FirstChar].TranslatedPic != nullptr) + { + if (LastChar < 0x1e9e) + { + Chars.Resize(0x1e9f - FirstChar); + LastChar = 0x1e9e; + } + if (Chars[0x1e9e - FirstChar].TranslatedPic == nullptr) + { + std::swap(Chars[0xdf - FirstChar], Chars[0x1e9e - FirstChar]); + lowercount--; + uppercount++; + if (lowercount == 0) return; + } + } +} + //========================================================================== // // FFont :: FindFont @@ -834,15 +879,6 @@ int FFont::GetCharCode(int code, bool needpic) const return code; } - // Special handling for the ß which may only exist as lowercase, so for this we need an additional upper -> lower check for all fonts aside from the generic substitution logic. - if (code == 0x1e9e) - { - if (LastChar <= 0xdf && (!needpic || Chars[0xdf - FirstChar].TranslatedPic != nullptr)) - { - return 0xdf; - } - } - // Use different substitution logic based on the fonts content: // In a font which has both upper and lower case, prefer unaccented small characters over capital ones. // In a pure upper-case font, do not check for lower case replacements. @@ -975,6 +1011,47 @@ double GetBottomAlignOffset(FFont *font, int c) return offset; } +//========================================================================== +// +// Checks if the font contains proper glyphs for all characters in the string +// +//========================================================================== + +bool FFont::CanPrint(const uint8_t *string) const +{ + while (*string) + { + auto chr = GetCharFromString(string); + if (!MixedCase) chr = upperforlower[chr]; // For uppercase-only fonts we shouldn't check lowercase characters. + if (chr == TEXTCOLOR_ESCAPE) + { + // We do not need to check for UTF-8 in here. + if (*string == '[') + { + while (*string != '\0' && *string != ']') + { + ++string; + } + } + if (*string != '\0') + { + ++string; + } + continue; + } + else if (chr != '\n') + { + int cc = GetCharCode(chr, true); + if (chr != cc) + { + return false; + } + } + } + + return true; +} + //========================================================================== // // Find string width using this font diff --git a/src/gamedata/fonts/v_font.cpp b/src/gamedata/fonts/v_font.cpp index c41674048..8aef25240 100644 --- a/src/gamedata/fonts/v_font.cpp +++ b/src/gamedata/fonts/v_font.cpp @@ -849,7 +849,7 @@ int stripaccent(int code) else if (code == 0x171) code = 0xfc; else { - static const char accentless[] = "AaAaAaCcCcCcCcDdDdEeEeEeEeEeGgGgGgGgHhHhIiIiIiIiIiIiJjKkkLlLlLlLlLlNnNnNnnNnOoOoOoOoRrRrRrSsSsSsSsTtTtTtUuUuUuUuUuUuWwYyYZzZzZz "; + static const char accentless[] = "AaAaAaCcCcCcCcDdDdEeEeEeEeEeGgGgGgGgHhHhIiIiIiIiIiIiJjKkkLlLlLlLlLlNnNnNnnNnOoOoOoOoRrRrRrSsSsSsSsTtTtTtUuUuUuUuUuUuWwYyYZzZzZzs"; return accentless[code - 0x100]; } } @@ -908,7 +908,7 @@ int stripaccent(int code) } - // skip the rest of Latin characters because none of them are relevant for modern languages. + // skip the rest of Latin characters because none of them are relevant for modern languages, except Vietnamese which cannot be represented with the tiny bitmap fonts anyway. return code; } diff --git a/src/gamedata/fonts/v_font.h b/src/gamedata/fonts/v_font.h index 5a799c1fd..a4486cf37 100644 --- a/src/gamedata/fonts/v_font.h +++ b/src/gamedata/fonts/v_font.h @@ -113,6 +113,11 @@ public: inline int StringWidth (const char *str) const { return StringWidth ((const uint8_t *)str); } inline int StringWidth (const FString &str) const { return StringWidth ((const uint8_t *)str.GetChars()); } + // Checks if the font contains all characters to print this text. + bool CanPrint(const uint8_t *str) const; + inline bool CanPrint(const char *str) const { return CanPrint((const uint8_t *)str); } + inline bool CanPrint(const FString &str) const { return CanPrint((const uint8_t *)str.GetChars()); } + int GetCharCode(int code, bool needpic) const; char GetCursor() const { return Cursor; } void SetCursor(char c) { Cursor = c; } @@ -120,6 +125,7 @@ public: bool NoTranslate() const { return noTranslate; } void RecordAllTextureColors(uint32_t *usedcolors); virtual void SetDefaultTranslation(uint32_t *colors); + void CheckCase(); protected: FFont (int lump); diff --git a/src/scripting/vmthunks.cpp b/src/scripting/vmthunks.cpp index df5169b1a..6046ff585 100644 --- a/src/scripting/vmthunks.cpp +++ b/src/scripting/vmthunks.cpp @@ -2021,6 +2021,19 @@ DEFINE_ACTION_FUNCTION_NATIVE(FFont, StringWidth, StringWidth) ACTION_RETURN_INT(StringWidth(self, str)); } +static int CanPrint(FFont *font, const FString &str) +{ + const char *txt = str[0] == '$' ? GStrings(&str[1]) : str.GetChars(); + return font->CanPrint(txt); +} + +DEFINE_ACTION_FUNCTION_NATIVE(FFont, CanPrint, CanPrint) +{ + PARAM_SELF_STRUCT_PROLOGUE(FFont); + PARAM_STRING(str); + ACTION_RETURN_INT(CanPrint(self, str)); +} + static int FindFontColor(int name) { return V_FindFontColor(ENamedName(name)); diff --git a/wadsrc/static/zscript/base.zs b/wadsrc/static/zscript/base.zs index b52548d6d..4992b503a 100644 --- a/wadsrc/static/zscript/base.zs +++ b/wadsrc/static/zscript/base.zs @@ -308,6 +308,7 @@ struct Font native native int GetCharWidth(int code); native int StringWidth(String code); + native bool CanPrint(String code); native int GetHeight(); native String GetCursor(); diff --git a/wadsrc/static/zscript/ui/statusbar/statusbar.zs b/wadsrc/static/zscript/ui/statusbar/statusbar.zs index 18f04af7f..70415e898 100644 --- a/wadsrc/static/zscript/ui/statusbar/statusbar.zs +++ b/wadsrc/static/zscript/ui/statusbar/statusbar.zs @@ -828,7 +828,7 @@ class BaseStatusBar native ui // automap HUD common drawer // This is not called directly to give a status bar the opportunity to // change the text colors. If you want to do something different, - // override DrawAutomap directly. + // override DrawAutomapHUD directly. // //============================================================================