mirror of
https://github.com/OpenTTD/OpenTTD.git
synced 2026-07-15 15:19:52 +00:00
Change: test font for eligibility before adding as fallback
Avoids reloading fonts and scanning for missing glyphs for each searched font.
This commit is contained in:
committed by
Peter Nelson
parent
9a0af4387b
commit
f864aaf131
@@ -246,6 +246,33 @@ static std::string GetFontCacheFontName(FontSize fs)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test a fallback font, with optional OS-specific handle, for specific glyphs.
|
||||
* @param fontsizes The fontsizes the font will be used for.
|
||||
* @param glyphs Glyphs to search for.
|
||||
* @param name Name of font to test.
|
||||
* @param os_handle OS-specific handle or data of font.
|
||||
* @return true iff the font can be used.
|
||||
*/
|
||||
/* static */ bool FontCache::TryFallback(FontSizes, const std::set<char32_t> &glyphs, const std::string &name, const std::any &os_handle)
|
||||
{
|
||||
/* Load the font without registering it. The font size does not matter. */
|
||||
auto fc = FontProviderManager::LoadFont(FS_NORMAL, FontType::TrueType, false, name, os_handle);
|
||||
if (fc == nullptr) return false;
|
||||
|
||||
size_t matching_chars = 0;
|
||||
for (const char32_t &c : glyphs) {
|
||||
if (fc->MapCharToGlyph(c, false) != 0) ++matching_chars;
|
||||
}
|
||||
|
||||
if (matching_chars < glyphs.size()) {
|
||||
Debug(fontcache, 1, "Font \"{}\" misses {} glyphs", name, glyphs.size() - matching_chars);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* (Re)initialize the font cache related things, i.e. load the non-sprite fonts.
|
||||
* @param fontsizes Font sizes to be initialised.
|
||||
|
||||
@@ -50,6 +50,8 @@ public:
|
||||
|
||||
static void AddFallback(FontSizes fontsizes, std::string_view name, const std::any &os_handle = {});
|
||||
|
||||
static bool TryFallback(FontSizes fontsizes, const std::set<char32_t> &glyphs, const std::string &name, const std::any &os_handle = {});
|
||||
|
||||
/**
|
||||
* Get the FontSize of the font.
|
||||
* @return The FontSize.
|
||||
|
||||
@@ -215,7 +215,7 @@ public:
|
||||
* something, no matter the name. As such, we can't use it to check for existence.
|
||||
* We instead query the list of all font descriptors that match the given name which
|
||||
* does not do this stupid name fallback. */
|
||||
CFAutoRelease<CTFontDescriptorRef> name_desc(CTFontDescriptorCreateWithNameAndSize(font_name.get(), 0.0));
|
||||
CFAutoRelease<CTFontDescriptorRef> name_desc(CTFontDescriptorCreateWithNameAndSize(name.get(), 0.0));
|
||||
CFAutoRelease<CFSetRef> mandatory_attribs(CFSetCreate(kCFAllocatorDefault, const_cast<const void **>(reinterpret_cast<const void * const *>(&kCTFontNameAttribute)), 1, &kCFTypeSetCallBacks));
|
||||
CFAutoRelease<CFArrayRef> descs(CTFontDescriptorCreateMatchingFontDescriptors(name_desc.get(), mandatory_attribs.get()));
|
||||
|
||||
@@ -298,10 +298,10 @@ public:
|
||||
if (name.starts_with(".") || name.starts_with("LastResort")) continue;
|
||||
|
||||
/* Save result. */
|
||||
FontCache::AddFallback(callback->missing_fontsizes, name);
|
||||
if (!callback->FindMissingGlyphs()) {
|
||||
result = FontCache::TryFallback(callback->missing_fontsizes, callback->missing_glyphs, std::string{name});
|
||||
if (result) {
|
||||
FontCache::AddFallback(callback->missing_fontsizes, name);
|
||||
Debug(fontcache, 2, "CT-Font for {}: {}", language_isocode, name);
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -310,11 +310,12 @@ public:
|
||||
if (!result) {
|
||||
/* For some OS versions, the font 'Arial Unicode MS' does not report all languages it
|
||||
* supports. If we didn't find any other font, just try it, maybe we get lucky. */
|
||||
FontCache::AddFallback(callback->missing_fontsizes, "Arial Unicode MS");
|
||||
result = !callback->FindMissingGlyphs();
|
||||
result = FontCache::TryFallback(callback->missing_fontsizes, callback->missing_glyphs, "Arial Unicode MS");
|
||||
if (result) {
|
||||
FontCache::AddFallback(callback->missing_fontsizes, "Arial Unicode MS");
|
||||
}
|
||||
}
|
||||
|
||||
callback->FindMissingGlyphs();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
+18
-11
@@ -162,7 +162,7 @@ bool FontConfigFindFallbackFont(const std::string &language_isocode, MissingGlyp
|
||||
/* First create a pattern to match the wanted language. */
|
||||
auto pat = AutoRelease<FcPattern, FcPatternDestroy>(FcNameParse(ToFcString(lang)));
|
||||
/* We only want to know these attributes. */
|
||||
auto os = AutoRelease<FcObjectSet, FcObjectSetDestroy>(FcObjectSetBuild(FC_FILE, FC_INDEX, FC_SPACING, FC_SLANT, FC_WEIGHT, nullptr));
|
||||
auto os = AutoRelease<FcObjectSet, FcObjectSetDestroy>(FcObjectSetBuild(FC_FILE, FC_INDEX, FC_SPACING, FC_SLANT, FC_WEIGHT, FC_CHARSET, nullptr));
|
||||
/* Get the list of filenames matching the wanted language. */
|
||||
auto fs = AutoRelease<FcFontSet, FcFontSetDestroy>(FcFontList(nullptr, pat.get(), os.get()));
|
||||
|
||||
@@ -191,21 +191,28 @@ bool FontConfigFindFallbackFont(const std::string &language_isocode, MissingGlyp
|
||||
int weight = GetPreferredWeightDistance(value);
|
||||
if (best_weight != -1 && weight > best_weight) continue;
|
||||
|
||||
/* Test the font for matching glyphs. This is essentially the same as
|
||||
* calling TryFallback() but avoids having to reopen the font, etc. */
|
||||
size_t matching_chars = 0;
|
||||
FcCharSet *charset;
|
||||
FcPatternGetCharSet(font, FC_CHARSET, 0, &charset);
|
||||
for (const char32_t &c : callback->missing_glyphs) {
|
||||
if (FcCharSetHasChar(charset, c)) ++matching_chars;
|
||||
}
|
||||
|
||||
if (matching_chars < callback->missing_glyphs.size()) {
|
||||
Debug(fontcache, 1, "Font \"{}\" misses {} glyphs", FromFcString(file), callback->missing_glyphs.size() - matching_chars);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Possible match based on attributes, get index. */
|
||||
int32_t index;
|
||||
res = FcPatternGetInteger(font, FC_INDEX, 0, &index);
|
||||
if (res != FcResultMatch) continue;
|
||||
|
||||
FontCache::AddFallback(callback->missing_fontsizes, FromFcString(file), index);
|
||||
|
||||
bool missing = callback->FindMissingGlyphs();
|
||||
Debug(fontcache, 1, "Font \"{}\" misses{} glyphs", FromFcString(file), missing ? "" : " no");
|
||||
|
||||
if (!missing) {
|
||||
best_weight = weight;
|
||||
best_font = FromFcString(file);
|
||||
best_index = index;
|
||||
}
|
||||
best_weight = weight;
|
||||
best_font = FromFcString(file);
|
||||
best_index = index;
|
||||
}
|
||||
|
||||
if (best_font == nullptr) return false;
|
||||
|
||||
@@ -76,8 +76,9 @@ static int CALLBACK EnumFontCallback(const ENUMLOGFONTEX *logfont, const NEWTEXT
|
||||
char font_name[MAX_PATH];
|
||||
convert_from_fs(logfont->elfFullName, font_name);
|
||||
|
||||
if (!FontCache::TryFallback(info->callback->missing_fontsizes, info->callback->missing_glyphs, font_name, logfont->elfLogFont)) return 1;
|
||||
|
||||
FontCache::AddFallback(info->callback->missing_fontsizes, font_name, logfont->elfLogFont);
|
||||
if (info->callback->FindMissingGlyphs()) return 1;
|
||||
Debug(fontcache, 1, "Fallback font: {}", font_name);
|
||||
return 0; // stop enumerating
|
||||
}
|
||||
|
||||
+32
-31
@@ -2306,40 +2306,40 @@ std::string_view GetCurrentLanguageIsoCode()
|
||||
return _langpack.langpack->isocode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether there are glyphs missing in the current language.
|
||||
* @return If glyphs are missing, return \c true, else return \c false.
|
||||
*/
|
||||
bool MissingGlyphSearcher::FindMissingGlyphs()
|
||||
void BaseStringMissingGlyphSearcher::DetermineRequiredGlyphs(FontSizes fontsizes)
|
||||
{
|
||||
FontCache::LoadFontCaches(this->fontsizes);
|
||||
this->missing_fontsizes.Reset();
|
||||
this->missing_glyphs.clear();
|
||||
|
||||
this->Reset();
|
||||
for (auto text = this->NextString(); text.has_value(); text = this->NextString()) {
|
||||
FontSize size = this->DefaultSize();
|
||||
FontCache *fc = FontCache::Get(size);
|
||||
FontSize fs = this->DefaultSize();
|
||||
FontCache *fc = FontCache::Get(fs);
|
||||
for (char32_t c : Utf8View(*text)) {
|
||||
if (c >= SCC_FIRST_FONT && c <= SCC_LAST_FONT) {
|
||||
size = (FontSize)(c - SCC_FIRST_FONT);
|
||||
fc = FontCache::Get(size);
|
||||
} else if (!IsInsideMM(c, SCC_SPRITE_START, SCC_SPRITE_END) && IsPrintable(c) && !IsTextDirectionChar(c) && fc->MapCharToGlyph(c, false) == 0) {
|
||||
/* The character is printable, but not in the normal font. This is the case we were testing for. */
|
||||
Debug(fontcache, 0, "Font is missing glyphs to display char 0x{:X} in {} font size", static_cast<uint32_t>(c), FontSizeToName(size));
|
||||
|
||||
/* For now, assume all fontsizes need searching if any are missing. */
|
||||
this->missing_fontsizes = this->fontsizes;
|
||||
|
||||
return true;
|
||||
fs = (FontSize)(c - SCC_FIRST_FONT);
|
||||
fc = FontCache::Get(fs);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!fontsizes.Test(fs)) continue;
|
||||
if (!IsPrintable(c) || IsTextDirectionChar(c)) continue;
|
||||
if (IsInsideMM(c, SCC_SPRITE_START, SCC_SPRITE_END)) continue;
|
||||
if (fc->MapCharToGlyph(c, false) != 0) continue;
|
||||
|
||||
this->missing_fontsizes.Set(fs);
|
||||
this->missing_glyphs.insert(c);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Helper for searching through the language pack. */
|
||||
class LanguagePackGlyphSearcher : public MissingGlyphSearcher {
|
||||
class LanguagePackGlyphSearcher : public BaseStringMissingGlyphSearcher {
|
||||
public:
|
||||
LanguagePackGlyphSearcher() : MissingGlyphSearcher(FONTSIZES_REQUIRED) {}
|
||||
/**
|
||||
* Create this language pack glyph searcher.
|
||||
*/
|
||||
LanguagePackGlyphSearcher() : BaseStringMissingGlyphSearcher(FONTSIZES_REQUIRED) {}
|
||||
|
||||
private:
|
||||
uint i; ///< Iterator for the primary language tables.
|
||||
@@ -2388,7 +2388,12 @@ void CheckForMissingGlyphs(MissingGlyphSearcher *searcher)
|
||||
{
|
||||
static LanguagePackGlyphSearcher pack_searcher;
|
||||
if (searcher == nullptr) searcher = &pack_searcher;
|
||||
bool bad_font = searcher->FindMissingGlyphs();
|
||||
|
||||
FontCache::LoadFontCaches(searcher->fontsizes);
|
||||
|
||||
searcher->DetermineRequiredGlyphs(searcher->fontsizes);
|
||||
bool bad_font = searcher->missing_fontsizes.Any();
|
||||
|
||||
#if defined(WITH_FREETYPE) || defined(_WIN32) || defined(WITH_COCOA)
|
||||
if (bad_font) {
|
||||
/* We found an unprintable character... lets try whether we can find
|
||||
@@ -2397,6 +2402,9 @@ void CheckForMissingGlyphs(MissingGlyphSearcher *searcher)
|
||||
FontCacheSettings backup = _fcsettings;
|
||||
|
||||
bad_font = !FontProviderManager::FindFallbackFont(_langpack.langpack->isocode, searcher);
|
||||
if (!bad_font) {
|
||||
FontCache::LoadFontCaches(searcher->missing_fontsizes);
|
||||
}
|
||||
|
||||
_fcsettings = std::move(backup);
|
||||
|
||||
@@ -2410,13 +2418,6 @@ void CheckForMissingGlyphs(MissingGlyphSearcher *searcher)
|
||||
builder.Put("The current font is missing some of the characters used in the texts for this language. Using system fallback font instead.");
|
||||
ShowErrorMessage(GetEncodedString(STR_JUST_RAW_STRING, std::move(err_str)), {}, WL_WARNING);
|
||||
}
|
||||
|
||||
if (bad_font) {
|
||||
/* Our fallback font does miss characters too, so keep the
|
||||
* user chosen font as that is more likely to be any good than
|
||||
* the wild guess we made */
|
||||
FontCache::LoadFontCaches(searcher->fontsizes);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -2432,12 +2433,12 @@ void CheckForMissingGlyphs(MissingGlyphSearcher *searcher)
|
||||
ShowErrorMessage(GetEncodedString(STR_JUST_RAW_STRING, std::move(err_str)), {}, WL_WARNING);
|
||||
|
||||
/* Reset the font width */
|
||||
LoadStringWidthTable(searcher->fontsizes);
|
||||
LoadStringWidthTable(searcher->missing_fontsizes);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Update the font with cache */
|
||||
LoadStringWidthTable(searcher->fontsizes);
|
||||
LoadStringWidthTable(searcher->missing_fontsizes);
|
||||
|
||||
#if !(defined(WITH_ICU_I18N) && defined(WITH_HARFBUZZ)) && !defined(WITH_UNISCRIBE) && !defined(WITH_COCOA)
|
||||
/*
|
||||
|
||||
+20
-3
@@ -167,6 +167,25 @@ public:
|
||||
|
||||
const FontSizes fontsizes; ///< Font sizes this searcher will try to find.
|
||||
FontSizes missing_fontsizes{}; ///< Font sizes to actually search for.
|
||||
std::set<char32_t> missing_glyphs{}; ///< Glyphs to search for.
|
||||
|
||||
/**
|
||||
* Determine set of glyphs required for the current language.
|
||||
* @param fontsizes Font sizes to test.
|
||||
**/
|
||||
virtual void DetermineRequiredGlyphs(FontSizes fontsizes) = 0;
|
||||
};
|
||||
|
||||
/** Base for missing glyph searchers that look for missing glyphs in strings. */
|
||||
class BaseStringMissingGlyphSearcher : public MissingGlyphSearcher {
|
||||
public:
|
||||
/**
|
||||
* Create this string glyph searcher.
|
||||
* @param fontsizes Font sizes to consider.
|
||||
*/
|
||||
BaseStringMissingGlyphSearcher(FontSizes fontsizes) : MissingGlyphSearcher(fontsizes) {}
|
||||
|
||||
void DetermineRequiredGlyphs(FontSizes fontsizes) override;
|
||||
|
||||
/**
|
||||
* Get the next string to search through.
|
||||
@@ -184,10 +203,8 @@ public:
|
||||
* Reset the search, i.e. begin from the beginning again.
|
||||
*/
|
||||
virtual void Reset() = 0;
|
||||
|
||||
bool FindMissingGlyphs();
|
||||
};
|
||||
|
||||
void CheckForMissingGlyphs(MissingGlyphSearcher *search = nullptr);
|
||||
void CheckForMissingGlyphs(MissingGlyphSearcher *searcher = nullptr);
|
||||
|
||||
#endif /* STRINGS_FUNC_H */
|
||||
|
||||
@@ -83,7 +83,7 @@ static WindowDesc _textfile_desc(
|
||||
_nested_textfile_widgets
|
||||
);
|
||||
|
||||
TextfileWindow::TextfileWindow(Window *parent, TextfileType file_type) : Window(_textfile_desc), MissingGlyphSearcher(FS_MONO), file_type(file_type)
|
||||
TextfileWindow::TextfileWindow(Window *parent, TextfileType file_type) : Window(_textfile_desc), BaseStringMissingGlyphSearcher(FS_MONO), file_type(file_type)
|
||||
{
|
||||
/* Init of nested tree is deferred.
|
||||
* TextfileWindow::ConstructWindow must be called by the inheriting window. */
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@
|
||||
std::optional<std::string> GetTextfile(TextfileType type, Subdirectory dir, std::string_view filename);
|
||||
|
||||
/** Window for displaying a textfile */
|
||||
struct TextfileWindow : public Window, MissingGlyphSearcher {
|
||||
struct TextfileWindow : public Window, BaseStringMissingGlyphSearcher {
|
||||
TextfileType file_type{}; ///< Type of textfile to view.
|
||||
Scrollbar *vscroll = nullptr; ///< Vertical scrollbar.
|
||||
Scrollbar *hscroll = nullptr; ///< Horizontal scrollbar.
|
||||
|
||||
Reference in New Issue
Block a user