1
0
Fork 0
Peter Nelson 2025-07-18 11:04:57 +00:00 committed by GitHub
commit a243aa2fcf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 651 additions and 573 deletions

View File

@ -187,7 +187,6 @@ add_files(
fios_gui.cpp
fontcache.cpp
fontcache.h
fontdetection.h
framerate_gui.cpp
framerate_type.h
gamelog.cpp

View File

@ -2369,7 +2369,7 @@ static bool ConFont(std::span<std::string_view> argv)
FontCacheSubSetting *setting = GetFontCacheSubSetting(fs);
/* Make sure all non sprite fonts are loaded. */
if (!setting->font.empty() && !fc->HasParent()) {
InitFontCache(fs == FS_MONO);
FontCache::LoadFontCaches(fs);
fc = FontCache::Get(fs);
}
IConsolePrint(CC_DEFAULT, "{} font:", FontSizeToName(fs));

View File

@ -9,10 +9,8 @@
#include "stdafx.h"
#include "fontcache.h"
#include "fontdetection.h"
#include "blitter/factory.hpp"
#include "gfx_layout.h"
#include "fontcache/spritefontcache.h"
#include "openttd.h"
#include "settings_func.h"
#include "strings_func.h"
@ -30,22 +28,38 @@
FontCacheSettings _fcsettings;
/**
* Create a new font cache.
* @param fs The size of the font.
* Try loading a font with any fontcache factory.
* @param fs Font size to load.
* @param fonttype Font type requested.
* @return FontCache of the font if loaded, or nullptr.
*/
FontCache::FontCache(FontSize fs) : parent(FontCache::Get(fs)), fs(fs)
/* static */ std::unique_ptr<FontCache> FontProviderManager::LoadFont(FontSize fs, FontType fonttype)
{
assert(this->parent == nullptr || this->fs == this->parent->fs);
FontCache::caches[this->fs] = this;
Layouter::ResetFontCache(this->fs);
for (auto &provider : FontProviderManager::GetProviders()) {
auto fc = provider->LoadFont(fs, fonttype);
if (fc != nullptr) return fc;
}
return nullptr;
}
/** Clean everything up. */
FontCache::~FontCache()
/**
* We would like to have a fallback font as the current one
* doesn't contain all characters we need.
* This function must set all fonts of settings.
* @param settings the settings to overwrite the fontname of.
* @param language_isocode the language, e.g. en_GB.
* @param callback The function to call to check for missing glyphs.
* @return true if a font has been set, false otherwise.
*/
/* static */ bool FontProviderManager::FindFallbackFont(FontCacheSettings *settings, const std::string &language_isocode, MissingGlyphSearcher *callback)
{
assert(this->parent == nullptr || this->fs == this->parent->fs);
FontCache::caches[this->fs] = this->parent;
Layouter::ResetFontCache(this->fs);
for (auto &provider : FontProviderManager::GetProviders()) {
if (provider->FindFallbackFont(settings, language_isocode, callback)) {
return true;
}
}
return false;
}
int FontCache::GetDefaultFontHeight(FontSize fs)
@ -80,12 +94,16 @@ int GetCharacterHeight(FontSize size)
}
/* static */ FontCache *FontCache::caches[FS_END];
/* static */ std::array<std::unique_ptr<FontCache>, FS_END> FontCache::caches{};
/**
* Initialise font caches with the base sprite font cache for all sizes.
*/
/* static */ void FontCache::InitializeFontCaches()
{
for (FontSize fs = FS_BEGIN; fs != FS_END; fs++) {
if (FontCache::caches[fs] == nullptr) new SpriteFontCache(fs); /* FontCache inserts itself into to the cache. */
if (FontCache::Get(fs) != nullptr) continue;
FontCache::Register(FontProviderManager::LoadFont(fs, FontType::Sprite));
}
}
@ -126,25 +144,16 @@ void SetFont(FontSize fontsize, const std::string &font, uint size)
CheckForMissingGlyphs();
_fcsettings = std::move(backup);
} else {
InitFontCache(true);
FontCache::LoadFontCaches(fontsize);
}
LoadStringWidthTable(fontsize == FS_MONO);
LoadStringWidthTable(fontsize);
UpdateAllVirtCoords();
ReInitAllWindows(true);
if (_save_config) SaveToConfig();
}
#ifdef WITH_FREETYPE
extern void LoadFreeTypeFont(FontSize fs);
extern void UninitFreeType();
#elif defined(_WIN32)
extern void LoadWin32Font(FontSize fs);
#elif defined(WITH_COCOA)
extern void LoadCoreTextFont(FontSize fs);
#endif
/**
* Test if a font setting uses the default font.
* @return true iff the font is not configured and no fallback font data is present.
@ -212,44 +221,54 @@ std::string GetFontCacheFontName(FontSize fs)
}
/**
* (Re)initialize the font cache related things, i.e. load the non-sprite fonts.
* @param monospace Whether to initialise the monospace or regular fonts.
* Register a FontCache for its font size.
* @param fc FontCache to register.
*/
void InitFontCache(bool monospace)
/* static */ void FontCache::Register(std::unique_ptr<FontCache> &&fc)
{
if (fc == nullptr) return;
FontSize fs = fc->fs;
fc->parent = std::move(FontCache::caches[fs]);
FontCache::caches[fs] = std::move(fc);
}
/**
* (Re)initialize the font cache related things, i.e. load the non-sprite fonts.
* @param fontsizes Font sizes to be initialised.
*/
/* static */ void FontCache::LoadFontCaches(FontSizes fontsizes)
{
FontCache::InitializeFontCaches();
for (FontSize fs = FS_BEGIN; fs < FS_END; fs++) {
if (monospace != (fs == FS_MONO)) continue;
for (FontSize fs : fontsizes) {
Layouter::ResetFontCache(fs);
FontCache *fc = FontCache::Get(fs);
if (fc->HasParent()) delete fc;
/* Unload everything except the sprite font cache. */
while (FontCache::Get(fs)->HasParent()) {
FontCache::caches[fs] = std::move(FontCache::caches[fs]->parent);
}
#ifdef WITH_FREETYPE
LoadFreeTypeFont(fs);
#elif defined(_WIN32)
LoadWin32Font(fs);
#elif defined(WITH_COCOA)
LoadCoreTextFont(fs);
#endif
FontCache::Register(FontProviderManager::LoadFont(fs, FontType::TrueType));
}
}
/**
* Clear cached information for the specified font caches.
* @param fontsizes Font sizes to clear.
*/
/* static */ void FontCache::ClearFontCaches(FontSizes fontsizes)
{
for (FontSize fs : fontsizes) {
FontCache::Get(fs)->ClearFontCache();
}
}
/**
* Free everything allocated w.r.t. fonts.
*/
void UninitFontCache()
/* static */ void FontCache::UninitializeFontCaches()
{
for (FontSize fs = FS_BEGIN; fs < FS_END; fs++) {
while (FontCache::Get(fs) != nullptr) delete FontCache::Get(fs);
}
#ifdef WITH_FREETYPE
UninitFreeType();
#endif /* WITH_FREETYPE */
std::ranges::generate(FontCache::caches, []() { return nullptr; });
}
#if !defined(_WIN32) && !defined(__APPLE__) && !defined(WITH_FONTCONFIG) && !defined(WITH_COCOA)
bool SetFallbackFont(FontCacheSettings *, const std::string &, MissingGlyphSearcher *) { return false; }
#endif /* !defined(_WIN32) && !defined(__APPLE__) && !defined(WITH_FONTCONFIG) && !defined(WITH_COCOA) */

View File

@ -10,6 +10,7 @@
#ifndef FONTCACHE_H
#define FONTCACHE_H
#include "provider_manager.h"
#include "string_type.h"
#include "spritecache.h"
@ -20,18 +21,23 @@ static const GlyphID SPRITE_GLYPH = 1U << 30;
/** Font cache for basic fonts. */
class FontCache {
protected:
static FontCache *caches[FS_END]; ///< All the font caches.
FontCache *parent; ///< The parent of this font cache.
const FontSize fs; ///< The size of the font.
static std::array<std::unique_ptr<FontCache>, FS_END> caches; ///< All the font caches.
std::unique_ptr<FontCache>parent; ///< The parent of this font cache.
const FontSize fs; ///< The size of the font.
int height = 0; ///< The height of the font.
int ascender = 0; ///< The ascender value of the font.
int descender = 0; ///< The descender value of the font.
FontCache(FontSize fs) : fs(fs) {}
static void Register(std::unique_ptr<FontCache> &&fc);
public:
FontCache(FontSize fs);
virtual ~FontCache();
virtual ~FontCache() {}
static void InitializeFontCaches();
static void UninitializeFontCaches();
static void LoadFontCaches(FontSizes fontsizes);
static void ClearFontCaches(FontSizes fontsizes);
/** Default unscaled font heights. */
static const int DEFAULT_FONT_HEIGHT[FS_END];
@ -70,16 +76,6 @@ public:
*/
virtual int GetFontSize() const { return this->height; }
/**
* Map a SpriteID to the key
* @param key The key to map to.
* @param sprite The sprite that is being mapped.
*/
virtual void SetUnicodeGlyph(char32_t key, SpriteID sprite) = 0;
/** Initialize the glyph map */
virtual void InitializeUnicodeGlyphMap() = 0;
/** Clear the font cache. */
virtual void ClearFontCache() = 0;
@ -134,7 +130,7 @@ public:
static inline FontCache *Get(FontSize fs)
{
assert(fs < FS_END);
return FontCache::caches[fs];
return FontCache::caches[fs].get();
}
static std::string GetName(FontSize fs);
@ -153,27 +149,6 @@ public:
virtual bool IsBuiltInFont() = 0;
};
/** Map a SpriteID to the font size and key */
inline void SetUnicodeGlyph(FontSize size, char32_t key, SpriteID sprite)
{
FontCache::Get(size)->SetUnicodeGlyph(key, sprite);
}
/** Initialize the glyph map */
inline void InitializeUnicodeGlyphMap()
{
for (FontSize fs = FS_BEGIN; fs < FS_END; fs++) {
FontCache::Get(fs)->InitializeUnicodeGlyphMap();
}
}
inline void ClearFontCache()
{
for (FontSize fs = FS_BEGIN; fs < FS_END; fs++) {
FontCache::Get(fs)->ClearFontCache();
}
}
/** Get the Sprite for a glyph */
inline const Sprite *GetGlyph(FontSize size, char32_t key)
{
@ -231,10 +206,41 @@ inline FontCacheSubSetting *GetFontCacheSubSetting(FontSize fs)
uint GetFontCacheFontSize(FontSize fs);
std::string GetFontCacheFontName(FontSize fs);
void InitFontCache(bool monospace);
void UninitFontCache();
bool GetFontAAState();
void SetFont(FontSize fontsize, const std::string &font, uint size);
/** Different types of font that can be loaded. */
enum class FontType : uint8_t {
Sprite, ///< Bitmap sprites from GRF files.
TrueType, ///< Scalable TrueType fonts.
};
/** Factory for FontCaches. */
class FontCacheFactory : public BaseProvider<FontCacheFactory> {
public:
FontCacheFactory(std::string_view name, std::string_view description) : BaseProvider<FontCacheFactory>(name, description)
{
ProviderManager<FontCacheFactory>::Register(*this);
}
virtual ~FontCacheFactory()
{
ProviderManager<FontCacheFactory>::Unregister(*this);
}
virtual std::unique_ptr<FontCache> LoadFont(FontSize fs, FontType fonttype) = 0;
virtual bool FindFallbackFont(struct FontCacheSettings *settings, const std::string &language_isocode, class MissingGlyphSearcher *callback) = 0;
};
class FontProviderManager : ProviderManager<FontCacheFactory> {
public:
static std::unique_ptr<FontCache> LoadFont(FontSize fs, FontType fonttype);
static bool FindFallbackFont(FontCacheSettings *settings, const std::string &language_isocode, MissingGlyphSearcher *callback);
};
/* Implemented in spritefontcache.cpp */
void InitializeUnicodeGlyphMap();
void SetUnicodeGlyph(FontSize size, char32_t key, SpriteID sprite);
#endif /* FONTCACHE_H */

View File

@ -8,14 +8,14 @@
/** @file freetypefontcache.cpp FreeType font cache implementation. */
#include "../stdafx.h"
#include "../debug.h"
#include "../fontcache.h"
#include "../fontdetection.h"
#include "../blitter/factory.hpp"
#include "../core/math_func.hpp"
#include "../zoom_func.h"
#include "../fileio_func.h"
#include "../error_func.h"
#include "../../os/unix/font_unix.h"
#include "truetypefontcache.h"
#include "../table/control_codes.h"
@ -46,9 +46,6 @@ public:
const void *GetOSHandle() override { return &face; }
};
FT_Library _ft_library = nullptr;
/**
* Create a new FreeTypeFontCache.
* @param fs The font size that is going to be cached.
@ -113,93 +110,6 @@ void FreeTypeFontCache::SetFontSize(int pixels)
}
}
static FT_Error LoadFont(FontSize fs, FT_Face face, std::string_view font_name, uint size)
{
Debug(fontcache, 2, "Requested '{}', using '{} {}'", font_name, face->family_name, face->style_name);
/* Attempt to select the unicode character map */
FT_Error error = FT_Select_Charmap(face, ft_encoding_unicode);
if (error == FT_Err_Ok) goto found_face; // Success
if (error == FT_Err_Invalid_CharMap_Handle) {
/* Try to pick a different character map instead. We default to
* the first map, but platform_id 0 encoding_id 0 should also
* be unicode (strange system...) */
FT_CharMap found = face->charmaps[0];
int i;
for (i = 0; i < face->num_charmaps; i++) {
FT_CharMap charmap = face->charmaps[i];
if (charmap->platform_id == 0 && charmap->encoding_id == 0) {
found = charmap;
}
}
if (found != nullptr) {
error = FT_Set_Charmap(face, found);
if (error == FT_Err_Ok) goto found_face;
}
}
FT_Done_Face(face);
return error;
found_face:
new FreeTypeFontCache(fs, face, size);
return FT_Err_Ok;
}
/**
* Loads the freetype font.
* First type to load the fontname as if it were a path. If that fails,
* try to resolve the filename of the font using fontconfig, where the
* format is 'font family name' or 'font family name, font style'.
* @param fs The font size to load.
*/
void LoadFreeTypeFont(FontSize fs)
{
FontCacheSubSetting *settings = GetFontCacheSubSetting(fs);
std::string font = GetFontCacheFontName(fs);
if (font.empty()) return;
if (_ft_library == nullptr) {
if (FT_Init_FreeType(&_ft_library) != FT_Err_Ok) {
ShowInfo("Unable to initialize FreeType, using sprite fonts instead");
return;
}
Debug(fontcache, 2, "Initialized");
}
FT_Face face = nullptr;
/* If font is an absolute path to a ttf, try loading that first. */
int32_t index = 0;
if (settings->os_handle != nullptr) index = *static_cast<const int32_t *>(settings->os_handle);
FT_Error error = FT_New_Face(_ft_library, font.c_str(), index, &face);
if (error != FT_Err_Ok) {
/* Check if font is a relative filename in one of our search-paths. */
std::string full_font = FioFindFullPath(BASE_DIR, font);
if (!full_font.empty()) {
error = FT_New_Face(_ft_library, full_font.c_str(), 0, &face);
}
}
/* Try loading based on font face name (OS-wide fonts). */
if (error != FT_Err_Ok) error = GetFontByFaceName(font, &face);
if (error == FT_Err_Ok) {
error = LoadFont(fs, face, font, GetFontCacheFontSize(fs));
if (error != FT_Err_Ok) {
ShowInfo("Unable to use '{}' for {} font, FreeType reported error 0x{:X}, using sprite font instead", font, FontSizeToName(fs), error);
}
} else {
FT_Done_Face(face);
}
}
/**
* Free everything that was allocated for this font cache.
*/
@ -296,19 +206,116 @@ GlyphID FreeTypeFontCache::MapCharToGlyph(char32_t key, bool allow_fallback)
return glyph;
}
/**
* Free everything allocated w.r.t. freetype.
*/
void UninitFreeType()
{
FT_Done_FreeType(_ft_library);
_ft_library = nullptr;
}
FT_Library _ft_library = nullptr;
#if !defined(WITH_FONTCONFIG)
class FreeTypeFontCacheFactory : public FontCacheFactory {
public:
FreeTypeFontCacheFactory() : FontCacheFactory("freetype", "FreeType font provider") {}
FT_Error GetFontByFaceName(std::string_view font_name, FT_Face *face) { return FT_Err_Cannot_Open_Resource; }
virtual ~FreeTypeFontCacheFactory()
{
FT_Done_FreeType(_ft_library);
_ft_library = nullptr;
}
#endif /* !defined(WITH_FONTCONFIG) */
/**
* Loads the freetype font.
* First type to load the fontname as if it were a path. If that fails,
* try to resolve the filename of the font using fontconfig, where the
* format is 'font family name' or 'font family name, font style'.
* @param fs The font size to load.
*/
std::unique_ptr<FontCache> LoadFont(FontSize fs, FontType fonttype) override
{
if (fonttype != FontType::TrueType) return nullptr;
FontCacheSubSetting *settings = GetFontCacheSubSetting(fs);
std::string font = GetFontCacheFontName(fs);
if (font.empty()) return nullptr;
if (_ft_library == nullptr) {
if (FT_Init_FreeType(&_ft_library) != FT_Err_Ok) {
ShowInfo("Unable to initialize FreeType, using sprite fonts instead");
return nullptr;
}
Debug(fontcache, 2, "Initialized");
}
FT_Face face = nullptr;
/* If font is an absolute path to a ttf, try loading that first. */
int32_t index = 0;
if (settings->os_handle != nullptr) index = *static_cast<const int32_t *>(settings->os_handle);
FT_Error error = FT_New_Face(_ft_library, font.c_str(), index, &face);
if (error != FT_Err_Ok) {
/* Check if font is a relative filename in one of our search-paths. */
std::string full_font = FioFindFullPath(BASE_DIR, font);
if (!full_font.empty()) {
error = FT_New_Face(_ft_library, full_font.c_str(), 0, &face);
}
}
#ifdef WITH_FONTCONFIG
/* Try loading based on font face name (OS-wide fonts). */
if (error != FT_Err_Ok) error = GetFontByFaceName(font, &face);
#endif /* WITH_FONTCONFIG */
if (error != FT_Err_Ok) {
FT_Done_Face(face);
return nullptr;
}
return LoadFont(fs, face, font, GetFontCacheFontSize(fs));
}
bool FindFallbackFont(struct FontCacheSettings *settings, const std::string &language_isocode, class MissingGlyphSearcher *callback) override
{
#ifdef WITH_FONTCONFIG
if (FontConfigFindFallbackFont(settings, language_isocode, callback)) return true;
#endif /* WITH_FONTCONFIG */
return false;
}
private:
static std::unique_ptr<FontCache> LoadFont(FontSize fs, FT_Face face, std::string_view font_name, uint size)
{
Debug(fontcache, 2, "Requested '{}', using '{} {}'", font_name, face->family_name, face->style_name);
/* Attempt to select the unicode character map */
FT_Error error = FT_Select_Charmap(face, ft_encoding_unicode);
if (error == FT_Err_Invalid_CharMap_Handle) {
/* Try to pick a different character map instead. We default to
* the first map, but platform_id 0 encoding_id 0 should also
* be unicode (strange system...) */
FT_CharMap found = face->charmaps[0];
for (int i = 0; i < face->num_charmaps; ++i) {
FT_CharMap charmap = face->charmaps[i];
if (charmap->platform_id == 0 && charmap->encoding_id == 0) {
found = charmap;
}
}
if (found != nullptr) {
error = FT_Set_Charmap(face, found);
}
}
if (error != FT_Err_Ok) {
FT_Done_Face(face);
ShowInfo("Unable to use '{}' for {} font, FreeType reported error 0x{:X}, using sprite font instead", font_name, FontSizeToName(fs), error);
return nullptr;
}
return std::make_unique<FreeTypeFontCache>(fs, face, size);
}
};
static FreeTypeFontCacheFactory s_freetype_fontcache_factory;
#endif /* WITH_FREETYPE */

View File

@ -10,6 +10,7 @@
#include "../stdafx.h"
#include "../fontcache.h"
#include "../gfx_layout.h"
#include "../string_func.h"
#include "../zoom_func.h"
#include "spritefontcache.h"
@ -31,41 +32,43 @@ static int ScaleFontTrad(int value)
return UnScaleByZoom(value * ZOOM_BASE, _font_zoom);
}
/**
* Create a new sprite font cache.
* @param fs The font size to create the cache for.
*/
SpriteFontCache::SpriteFontCache(FontSize fs) : FontCache(fs)
{
this->InitializeUnicodeGlyphMap();
this->height = ScaleGUITrad(FontCache::GetDefaultFontHeight(this->fs));
this->ascender = (this->height - ScaleFontTrad(FontCache::GetDefaultFontHeight(this->fs))) / 2;
}
static std::array<std::unordered_map<char32_t, SpriteID>, FS_END> _char_maps{}; ///< Glyph map for each font size.
/**
* Get SpriteID associated with a character.
* @param key Character to find.
* @return SpriteID for character, or 0 if not present.
*/
SpriteID SpriteFontCache::GetUnicodeGlyph(char32_t key)
static SpriteID GetUnicodeGlyph(FontSize fs, char32_t key)
{
const auto found = this->char_map.find(key);
if (found == std::end(this->char_map)) return 0;
return found->second;
auto found = _char_maps[fs].find(key);
if (found != std::end(_char_maps[fs])) return found->second;
return 0;
}
void SpriteFontCache::SetUnicodeGlyph(char32_t key, SpriteID sprite)
/**
* Set the SpriteID for a unicode character.
* @param fs Font size to set.
* @param key Unicode character to set.
* @param sprite SpriteID of character.
*/
void SetUnicodeGlyph(FontSize fs, char32_t key, SpriteID sprite)
{
this->char_map[key] = sprite;
_char_maps[fs][key] = sprite;
}
void SpriteFontCache::InitializeUnicodeGlyphMap()
/**
* Initialize the glyph map for a font size.
* This populates the glyph map with the baseset font sprites.
* @param fs Font size to initialize.
*/
void InitializeUnicodeGlyphMap(FontSize fs)
{
/* Clear out existing glyph map if it exists */
this->char_map.clear();
_char_maps[fs].clear();
SpriteID base;
switch (this->fs) {
switch (fs) {
default: NOT_REACHED();
case FS_MONO: // Use normal as default for mono spaced font
case FS_NORMAL: base = SPR_ASCII_SPACE; break;
@ -76,24 +79,45 @@ void SpriteFontCache::InitializeUnicodeGlyphMap()
for (uint i = ASCII_LETTERSTART; i < 256; i++) {
SpriteID sprite = base + i - ASCII_LETTERSTART;
if (!SpriteExists(sprite)) continue;
this->SetUnicodeGlyph(i, sprite);
this->SetUnicodeGlyph(i + SCC_SPRITE_START, sprite);
SetUnicodeGlyph(fs, i, sprite);
SetUnicodeGlyph(fs, i + SCC_SPRITE_START, sprite);
}
/* Modify map to move non-standard glyphs to a better unicode codepoint. */
for (const auto &unicode_map : _default_unicode_map) {
uint8_t key = unicode_map.key;
if (key == CLRA) {
/* Clear the glyph. This happens if the glyph at this code point
* is non-standard and should be accessed by an SCC_xxx enum
* entry only. */
this->SetUnicodeGlyph(unicode_map.code, 0);
SetUnicodeGlyph(fs, unicode_map.code, 0);
} else {
SpriteID sprite = base + key - ASCII_LETTERSTART;
this->SetUnicodeGlyph(unicode_map.code, sprite);
SetUnicodeGlyph(fs, unicode_map.code, sprite);
}
}
}
/**
* Initialize the glyph map.
*/
void InitializeUnicodeGlyphMap()
{
for (FontSize fs = FS_BEGIN; fs < FS_END; fs++) {
InitializeUnicodeGlyphMap(fs);
}
}
/**
* Create a new sprite font cache.
* @param fs The font size to create the cache for.
*/
SpriteFontCache::SpriteFontCache(FontSize fs) : FontCache(fs)
{
this->height = ScaleGUITrad(FontCache::GetDefaultFontHeight(this->fs));
this->ascender = (this->height - ScaleFontTrad(FontCache::GetDefaultFontHeight(this->fs))) / 2;
}
void SpriteFontCache::ClearFontCache()
{
Layouter::ResetFontCache(this->fs);
@ -104,21 +128,21 @@ void SpriteFontCache::ClearFontCache()
const Sprite *SpriteFontCache::GetGlyph(GlyphID key)
{
SpriteID sprite = static_cast<SpriteID>(key & ~SPRITE_GLYPH);
if (sprite == 0) sprite = this->GetUnicodeGlyph('?');
if (sprite == 0) sprite = GetUnicodeGlyph(this->fs, '?');
return GetSprite(sprite, SpriteType::Font);
}
uint SpriteFontCache::GetGlyphWidth(GlyphID key)
{
SpriteID sprite = static_cast<SpriteID>(key & ~SPRITE_GLYPH);
if (sprite == 0) sprite = this->GetUnicodeGlyph('?');
if (sprite == 0) sprite = GetUnicodeGlyph(this->fs, '?');
return SpriteExists(sprite) ? GetSprite(sprite, SpriteType::Font)->width + ScaleFontTrad(this->fs != FS_NORMAL ? 1 : 0) : 0;
}
GlyphID SpriteFontCache::MapCharToGlyph(char32_t key, [[maybe_unused]] bool allow_fallback)
{
assert(IsPrintable(key));
SpriteID sprite = this->GetUnicodeGlyph(key);
SpriteID sprite = GetUnicodeGlyph(this->fs, key);
if (sprite == 0) return 0;
return SPRITE_GLYPH | sprite;
}
@ -127,3 +151,22 @@ bool SpriteFontCache::GetDrawGlyphShadow()
{
return false;
}
class SpriteFontCacheFactory : public FontCacheFactory {
public:
SpriteFontCacheFactory() : FontCacheFactory("sprite", "Sprite font provider") {}
std::unique_ptr<FontCache> LoadFont(FontSize fs, FontType fonttype) override
{
if (fonttype != FontType::Sprite) return nullptr;
return std::make_unique<SpriteFontCache>(fs);
}
bool FindFallbackFont(struct FontCacheSettings *, const std::string &, class MissingGlyphSearcher *) override
{
return false;
}
};
static SpriteFontCacheFactory s_sprite_fontcache_factory;

View File

@ -10,15 +10,12 @@
#ifndef SPRITEFONTCACHE_H
#define SPRITEFONTCACHE_H
#include "../string_func.h"
#include "../fontcache.h"
/** Font cache for fonts that are based on a freetype font. */
class SpriteFontCache : public FontCache {
public:
SpriteFontCache(FontSize fs);
void SetUnicodeGlyph(char32_t key, SpriteID sprite) override;
void InitializeUnicodeGlyphMap() override;
void ClearFontCache() override;
const Sprite *GetGlyph(GlyphID key) override;
uint GetGlyphWidth(GlyphID key) override;
@ -26,10 +23,6 @@ public:
GlyphID MapCharToGlyph(char32_t key, bool allow_fallback = true) override;
std::string GetFontName() override { return "sprite"; }
bool IsBuiltInFont() override { return true; }
private:
std::unordered_map<char32_t, SpriteID> char_map{}; ///< Mapping of characters to sprite IDs.
SpriteID GetUnicodeGlyph(char32_t key);
};
#endif /* SPRITEFONTCACHE_H */

View File

@ -46,8 +46,6 @@ public:
TrueTypeFontCache(FontSize fs, int pixels);
virtual ~TrueTypeFontCache();
int GetFontSize() const override { return this->used_size; }
void SetUnicodeGlyph(char32_t key, SpriteID sprite) override { this->parent->SetUnicodeGlyph(key, sprite); }
void InitializeUnicodeGlyphMap() override { this->parent->InitializeUnicodeGlyphMap(); }
const Sprite *GetGlyph(GlyphID key) override;
void ClearFontCache() override;
uint GetGlyphWidth(GlyphID key) override;

View File

@ -1,41 +0,0 @@
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file fontdetection.h Functions related to detecting/finding the right font. */
#ifndef FONTDETECTION_H
#define FONTDETECTION_H
#include "fontcache.h"
#ifdef WITH_FREETYPE
#include <ft2build.h>
#include FT_FREETYPE_H
/**
* Load a freetype font face with the given font name.
* @param font_name The name of the font to load.
* @param face The face that has been found.
* @return The error we encountered.
*/
FT_Error GetFontByFaceName(std::string_view font_name, FT_Face *face);
#endif /* WITH_FREETYPE */
/**
* We would like to have a fallback font as the current one
* doesn't contain all characters we need.
* This function must set all fonts of settings.
* @param settings the settings to overwrite the fontname of.
* @param language_isocode the language, e.g. en_GB.
* @param callback The function to call to check for missing glyphs.
* @return true if a font has been set, false otherwise.
*/
bool SetFallbackFont(struct FontCacheSettings *settings, const std::string &language_isocode, class MissingGlyphSearcher *callback);
#endif

View File

@ -8,6 +8,7 @@
/** @file gfx.cpp Handling of drawing text and other gfx related stuff. */
#include "stdafx.h"
#include "gfx_func.h"
#include "gfx_layout.h"
#include "progress.h"
#include "zoom_func.h"
@ -1240,14 +1241,14 @@ static void GfxMainBlitter(const Sprite *sprite, int x, int y, BlitterMode mode,
}
/**
* Initialize _stringwidth_table cache
* @param monospace Whether to load the monospace cache or the normal fonts.
* Initialize _stringwidth_table cache for the specified font sizes.
* @param fontsizes Font sizes to initialise.
*/
void LoadStringWidthTable(bool monospace)
void LoadStringWidthTable(FontSizes fontsizes)
{
ClearFontCache();
FontCache::ClearFontCaches(fontsizes);
for (FontSize fs = monospace ? FS_MONO : FS_BEGIN; fs < (monospace ? FS_END : FS_MONO); fs++) {
for (FontSize fs : fontsizes) {
for (uint i = 0; i != 224; i++) {
_stringwidth_table[fs][i] = GetGlyphWidth(fs, i + 32);
}
@ -1812,7 +1813,7 @@ bool AdjustGUIZoom(bool automatic)
if (old_font_zoom != _font_zoom) {
GfxClearFontSpriteCache();
}
ClearFontCache();
FontCache::ClearFontCaches(FONTSIZES_ALL);
LoadStringWidthTable();
SetupWidgetDimensions();

View File

@ -149,7 +149,7 @@ int GetStringHeight(StringID str, int maxw);
int GetStringLineCount(std::string_view str, int maxw);
Dimension GetStringMultiLineBoundingBox(StringID str, const Dimension &suggestion);
Dimension GetStringMultiLineBoundingBox(std::string_view str, const Dimension &suggestion, FontSize fontsize = FS_NORMAL);
void LoadStringWidthTable(bool monospace = false);
void LoadStringWidthTable(FontSizes fontsizes = FONTSIZES_REQUIRED);
void DrawDirtyBlocks();
void AddDirtyBlock(int left, int top, int right, int bottom);

View File

@ -258,6 +258,13 @@ enum FontSize : uint8_t {
};
DECLARE_INCREMENT_DECREMENT_OPERATORS(FontSize)
using FontSizes = EnumBitSet<FontSize, uint8_t>;
/** Mask of all possible font sizes. */
constexpr FontSizes FONTSIZES_ALL{FS_NORMAL, FS_SMALL, FS_LARGE, FS_MONO};
/** Mask of font sizes required to be present. */
constexpr FontSizes FONTSIZES_REQUIRED{FS_NORMAL, FS_SMALL, FS_LARGE};
inline std::string_view FontSizeToName(FontSize fs)
{
static const std::string_view SIZE_TO_NAME[] = { "medium", "small", "large", "mono" };

View File

@ -245,7 +245,7 @@ static void RealChangeBlitter(std::string_view repl_blitter)
/* Clear caches that might have sprites for another blitter. */
VideoDriver::GetInstance()->ClearSystemSprites();
ClearFontCache();
FontCache::ClearFontCaches(FONTSIZES_ALL);
GfxClearSpriteCache();
ReInitAllWindows(false);
}
@ -326,7 +326,7 @@ void CheckBlitter()
{
if (!SwitchNewGRFBlitter()) return;
ClearFontCache();
FontCache::ClearFontCaches(FONTSIZES_ALL);
GfxClearSpriteCache();
ReInitAllWindows(false);
}
@ -338,7 +338,7 @@ void GfxLoadSprites()
SwitchNewGRFBlitter();
VideoDriver::GetInstance()->ClearSystemSprites();
ClearFontCache();
FontCache::ClearFontCaches(FONTSIZES_ALL);
GfxInitSpriteMem();
LoadSpriteTables();
GfxInitPalettes();

View File

@ -315,7 +315,7 @@ static void ShutdownGame()
/* No NewGRFs were loaded when it was still bootstrapping. */
if (_game_mode != GM_BOOTSTRAP) ResetNewGRFData();
UninitFontCache();
FontCache::UninitializeFontCaches();
}
/**
@ -700,7 +700,7 @@ int openttd_main(std::span<std::string_view> arguments)
InitializeLanguagePacks();
/* Initialize the font cache */
InitFontCache(false);
FontCache::LoadFontCaches(FONTSIZES_REQUIRED);
/* This must be done early, since functions use the SetWindowDirty* calls */
InitWindowSystem();

View File

@ -14,7 +14,6 @@
#include "../../blitter/factory.hpp"
#include "../../error_func.h"
#include "../../fileio_func.h"
#include "../../fontdetection.h"
#include "../../string_func.h"
#include "../../strings_func.h"
#include "../../zoom_func.h"
@ -24,91 +23,6 @@
#include "../../safeguards.h"
bool SetFallbackFont(FontCacheSettings *settings, const std::string &language_isocode, MissingGlyphSearcher *callback)
{
/* Determine fallback font using CoreText. This uses the language isocode
* to find a suitable font. CoreText is available from 10.5 onwards. */
std::string lang;
if (language_isocode == "zh_TW") {
/* Traditional Chinese */
lang = "zh-Hant";
} else if (language_isocode == "zh_CN") {
/* Simplified Chinese */
lang = "zh-Hans";
} else {
/* Just copy the first part of the isocode. */
lang = language_isocode.substr(0, language_isocode.find('_'));
}
/* Create a font descriptor matching the wanted language and latin (english) glyphs.
* Can't use CFAutoRelease here for everything due to the way the dictionary has to be created. */
CFStringRef lang_codes[2];
lang_codes[0] = CFStringCreateWithCString(kCFAllocatorDefault, lang.c_str(), kCFStringEncodingUTF8);
lang_codes[1] = CFSTR("en");
CFArrayRef lang_arr = CFArrayCreate(kCFAllocatorDefault, (const void **)lang_codes, lengthof(lang_codes), &kCFTypeArrayCallBacks);
CFAutoRelease<CFDictionaryRef> lang_attribs(CFDictionaryCreate(kCFAllocatorDefault, const_cast<const void **>(reinterpret_cast<const void *const *>(&kCTFontLanguagesAttribute)), (const void **)&lang_arr, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
CFAutoRelease<CTFontDescriptorRef> lang_desc(CTFontDescriptorCreateWithAttributes(lang_attribs.get()));
CFRelease(lang_arr);
CFRelease(lang_codes[0]);
/* Get array of all font descriptors for the wanted language. */
CFAutoRelease<CFSetRef> mandatory_attribs(CFSetCreate(kCFAllocatorDefault, const_cast<const void **>(reinterpret_cast<const void *const *>(&kCTFontLanguagesAttribute)), 1, &kCFTypeSetCallBacks));
CFAutoRelease<CFArrayRef> descs(CTFontDescriptorCreateMatchingFontDescriptors(lang_desc.get(), mandatory_attribs.get()));
bool result = false;
for (int tries = 0; tries < 2; tries++) {
for (CFIndex i = 0; descs.get() != nullptr && i < CFArrayGetCount(descs.get()); i++) {
CTFontDescriptorRef font = (CTFontDescriptorRef)CFArrayGetValueAtIndex(descs.get(), i);
/* Get font traits. */
CFAutoRelease<CFDictionaryRef> traits((CFDictionaryRef)CTFontDescriptorCopyAttribute(font, kCTFontTraitsAttribute));
CTFontSymbolicTraits symbolic_traits;
CFNumberGetValue((CFNumberRef)CFDictionaryGetValue(traits.get(), kCTFontSymbolicTrait), kCFNumberIntType, &symbolic_traits);
/* Skip symbol fonts and vertical fonts. */
if ((symbolic_traits & kCTFontClassMaskTrait) == (CTFontStylisticClass)kCTFontSymbolicClass || (symbolic_traits & kCTFontVerticalTrait)) continue;
/* Skip bold fonts (especially Arial Bold, which looks worse than regular Arial). */
if (symbolic_traits & kCTFontBoldTrait) continue;
/* Select monospaced fonts if asked for. */
if (((symbolic_traits & kCTFontMonoSpaceTrait) == kCTFontMonoSpaceTrait) != callback->Monospace()) continue;
/* Get font name. */
char buffer[128];
CFAutoRelease<CFStringRef> font_name((CFStringRef)CTFontDescriptorCopyAttribute(font, kCTFontDisplayNameAttribute));
CFStringGetCString(font_name.get(), buffer, std::size(buffer), kCFStringEncodingUTF8);
/* Serif fonts usually look worse on-screen with only small
* font sizes. As such, we try for a sans-serif font first.
* If we can't find one in the first try, try all fonts. */
if (tries == 0 && (symbolic_traits & kCTFontClassMaskTrait) != (CTFontStylisticClass)kCTFontSansSerifClass) continue;
/* There are some special fonts starting with an '.' and the last
* resort font that aren't usable. Skip them. */
std::string_view name{buffer};
if (name.starts_with(".") || name.starts_with("LastResort")) continue;
/* Save result. */
callback->SetFontNames(settings, name);
if (!callback->FindMissingGlyphs()) {
Debug(fontcache, 2, "CT-Font for {}: {}", language_isocode, name);
result = true;
break;
}
}
}
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. */
callback->SetFontNames(settings, "Arial Unicode MS");
result = !callback->FindMissingGlyphs();
}
callback->FindMissingGlyphs();
return result;
}
CoreTextFontCache::CoreTextFontCache(FontSize fs, CFAutoRelease<CTFontDescriptorRef> &&font, int pixels) : TrueTypeFontCache(fs, pixels), font_desc(std::move(font))
{
this->SetFontSize(pixels);
@ -287,88 +201,182 @@ const Sprite *CoreTextFontCache::InternalGetGlyph(GlyphID key, bool use_aa)
return this->SetGlyphPtr(key, std::move(new_glyph)).GetSprite();
}
static CTFontDescriptorRef LoadFontFromFile(const std::string &font_name)
{
if (!MacOSVersionIsAtLeast(10, 6, 0)) return nullptr;
class CoreTextFontCacheFactory : public FontCacheFactory {
public:
CoreTextFontCacheFactory() : FontCacheFactory("coretext", "CoreText font loader") {}
/* Might be a font file name, try load it. Direct font loading is
* only supported starting on OSX 10.6. */
CFAutoRelease<CFStringRef> path;
/**
* Loads the TrueType font.
* If a CoreText font description is present, e.g. from the automatic font
* fallback search, use it. Otherwise, try to resolve it by font name.
* @param fs The font size to load.
*/
std::unique_ptr<FontCache> LoadFont(FontSize fs, FontType fonttype) override
{
if (fonttype != FontType::TrueType) return nullptr;
/* See if this is an absolute path. */
if (FileExists(font_name)) {
path.reset(CFStringCreateWithCString(kCFAllocatorDefault, font_name.c_str(), kCFStringEncodingUTF8));
} else {
/* Scan the search-paths to see if it can be found. */
std::string full_font = FioFindFullPath(BASE_DIR, font_name);
if (!full_font.empty()) {
path.reset(CFStringCreateWithCString(kCFAllocatorDefault, full_font.c_str(), kCFStringEncodingUTF8));
FontCacheSubSetting *settings = GetFontCacheSubSetting(fs);
std::string font = GetFontCacheFontName(fs);
if (font.empty()) return nullptr;
CFAutoRelease<CTFontDescriptorRef> font_ref;
if (settings->os_handle != nullptr) {
font_ref.reset(static_cast<CTFontDescriptorRef>(const_cast<void *>(settings->os_handle)));
CFRetain(font_ref.get()); // Increase ref count to match a later release.
}
}
if (path) {
/* Try getting a font descriptor to see if the system can use it. */
CFAutoRelease<CFURLRef> url(CFURLCreateWithFileSystemPath(kCFAllocatorDefault, path.get(), kCFURLPOSIXPathStyle, false));
CFAutoRelease<CFArrayRef> descs(CTFontManagerCreateFontDescriptorsFromURL(url.get()));
if (descs && CFArrayGetCount(descs.get()) > 0) {
CTFontDescriptorRef font_ref = (CTFontDescriptorRef)CFArrayGetValueAtIndex(descs.get(), 0);
CFRetain(font_ref);
return font_ref;
if (!font_ref && MacOSVersionIsAtLeast(10, 6, 0)) {
/* Might be a font file name, try load it. */
font_ref.reset(LoadFontFromFile(font));
if (!font_ref) ShowInfo("Unable to load file '{}' for {} font, using default OS font selection instead", font, FontSizeToName(fs));
}
}
return nullptr;
}
if (!font_ref) {
CFAutoRelease<CFStringRef> name(CFStringCreateWithCString(kCFAllocatorDefault, font.c_str(), kCFStringEncodingUTF8));
/**
* Loads the TrueType font.
* If a CoreText font description is present, e.g. from the automatic font
* fallback search, use it. Otherwise, try to resolve it by font name.
* @param fs The font size to load.
*/
void LoadCoreTextFont(FontSize fs)
{
FontCacheSubSetting *settings = GetFontCacheSubSetting(fs);
/* Simply creating the font using CTFontCreateWithNameAndSize will *always* return
* 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(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()));
std::string font = GetFontCacheFontName(fs);
if (font.empty()) return;
CFAutoRelease<CTFontDescriptorRef> font_ref;
if (settings->os_handle != nullptr) {
font_ref.reset(static_cast<CTFontDescriptorRef>(const_cast<void *>(settings->os_handle)));
CFRetain(font_ref.get()); // Increase ref count to match a later release.
}
if (!font_ref && MacOSVersionIsAtLeast(10, 6, 0)) {
/* Might be a font file name, try load it. */
font_ref.reset(LoadFontFromFile(font));
if (!font_ref) ShowInfo("Unable to load file '{}' for {} font, using default OS font selection instead", font, FontSizeToName(fs));
}
if (!font_ref) {
CFAutoRelease<CFStringRef> name(CFStringCreateWithCString(kCFAllocatorDefault, font.c_str(), kCFStringEncodingUTF8));
/* Simply creating the font using CTFontCreateWithNameAndSize will *always* return
* 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(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()));
/* Assume the first result is the one we want. */
if (descs && CFArrayGetCount(descs.get()) > 0) {
font_ref.reset((CTFontDescriptorRef)CFArrayGetValueAtIndex(descs.get(), 0));
CFRetain(font_ref.get());
/* Assume the first result is the one we want. */
if (descs && CFArrayGetCount(descs.get()) > 0) {
font_ref.reset((CTFontDescriptorRef)CFArrayGetValueAtIndex(descs.get(), 0));
CFRetain(font_ref.get());
}
}
if (!font_ref) {
ShowInfo("Unable to use '{}' for {} font, using sprite font instead", font, FontSizeToName(fs));
return nullptr;
}
return std::make_unique<CoreTextFontCache>(fs, std::move(font_ref), GetFontCacheFontSize(fs));
}
if (!font_ref) {
ShowInfo("Unable to use '{}' for {} font, using sprite font instead", font, FontSizeToName(fs));
return;
bool FindFallbackFont(FontCacheSettings *settings, const std::string &language_isocode, MissingGlyphSearcher *callback) override
{
/* Determine fallback font using CoreText. This uses the language isocode
* to find a suitable font. CoreText is available from 10.5 onwards. */
std::string lang;
if (language_isocode == "zh_TW") {
/* Traditional Chinese */
lang = "zh-Hant";
} else if (language_isocode == "zh_CN") {
/* Simplified Chinese */
lang = "zh-Hans";
} else {
/* Just copy the first part of the isocode. */
lang = language_isocode.substr(0, language_isocode.find('_'));
}
/* Create a font descriptor matching the wanted language and latin (english) glyphs.
* Can't use CFAutoRelease here for everything due to the way the dictionary has to be created. */
CFStringRef lang_codes[2];
lang_codes[0] = CFStringCreateWithCString(kCFAllocatorDefault, lang.c_str(), kCFStringEncodingUTF8);
lang_codes[1] = CFSTR("en");
CFArrayRef lang_arr = CFArrayCreate(kCFAllocatorDefault, (const void **)lang_codes, lengthof(lang_codes), &kCFTypeArrayCallBacks);
CFAutoRelease<CFDictionaryRef> lang_attribs(CFDictionaryCreate(kCFAllocatorDefault, const_cast<const void **>(reinterpret_cast<const void *const *>(&kCTFontLanguagesAttribute)), (const void **)&lang_arr, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
CFAutoRelease<CTFontDescriptorRef> lang_desc(CTFontDescriptorCreateWithAttributes(lang_attribs.get()));
CFRelease(lang_arr);
CFRelease(lang_codes[0]);
/* Get array of all font descriptors for the wanted language. */
CFAutoRelease<CFSetRef> mandatory_attribs(CFSetCreate(kCFAllocatorDefault, const_cast<const void **>(reinterpret_cast<const void *const *>(&kCTFontLanguagesAttribute)), 1, &kCFTypeSetCallBacks));
CFAutoRelease<CFArrayRef> descs(CTFontDescriptorCreateMatchingFontDescriptors(lang_desc.get(), mandatory_attribs.get()));
bool result = false;
for (int tries = 0; tries < 2; tries++) {
for (CFIndex i = 0; descs.get() != nullptr && i < CFArrayGetCount(descs.get()); i++) {
CTFontDescriptorRef font = (CTFontDescriptorRef)CFArrayGetValueAtIndex(descs.get(), i);
/* Get font traits. */
CFAutoRelease<CFDictionaryRef> traits((CFDictionaryRef)CTFontDescriptorCopyAttribute(font, kCTFontTraitsAttribute));
CTFontSymbolicTraits symbolic_traits;
CFNumberGetValue((CFNumberRef)CFDictionaryGetValue(traits.get(), kCTFontSymbolicTrait), kCFNumberIntType, &symbolic_traits);
/* Skip symbol fonts and vertical fonts. */
if ((symbolic_traits & kCTFontClassMaskTrait) == (CTFontStylisticClass)kCTFontSymbolicClass || (symbolic_traits & kCTFontVerticalTrait)) continue;
/* Skip bold fonts (especially Arial Bold, which looks worse than regular Arial). */
if (symbolic_traits & kCTFontBoldTrait) continue;
/* Select monospaced fonts if asked for. */
if (((symbolic_traits & kCTFontMonoSpaceTrait) == kCTFontMonoSpaceTrait) != callback->Monospace()) continue;
/* Get font name. */
char buffer[128];
CFAutoRelease<CFStringRef> font_name((CFStringRef)CTFontDescriptorCopyAttribute(font, kCTFontDisplayNameAttribute));
CFStringGetCString(font_name.get(), buffer, std::size(buffer), kCFStringEncodingUTF8);
/* Serif fonts usually look worse on-screen with only small
* font sizes. As such, we try for a sans-serif font first.
* If we can't find one in the first try, try all fonts. */
if (tries == 0 && (symbolic_traits & kCTFontClassMaskTrait) != (CTFontStylisticClass)kCTFontSansSerifClass) continue;
/* There are some special fonts starting with an '.' and the last
* resort font that aren't usable. Skip them. */
std::string_view name{buffer};
if (name.starts_with(".") || name.starts_with("LastResort")) continue;
/* Save result. */
callback->SetFontNames(settings, name);
if (!callback->FindMissingGlyphs()) {
Debug(fontcache, 2, "CT-Font for {}: {}", language_isocode, name);
result = true;
break;
}
}
}
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. */
callback->SetFontNames(settings, "Arial Unicode MS");
result = !callback->FindMissingGlyphs();
}
callback->FindMissingGlyphs();
return result;
}
new CoreTextFontCache(fs, std::move(font_ref), GetFontCacheFontSize(fs));
}
private:
static CTFontDescriptorRef LoadFontFromFile(const std::string &font_name)
{
if (!MacOSVersionIsAtLeast(10, 6, 0)) return nullptr;
/* Might be a font file name, try load it. Direct font loading is
* only supported starting on OSX 10.6. */
CFAutoRelease<CFStringRef> path;
/* See if this is an absolute path. */
if (FileExists(font_name)) {
path.reset(CFStringCreateWithCString(kCFAllocatorDefault, font_name.c_str(), kCFStringEncodingUTF8));
} else {
/* Scan the search-paths to see if it can be found. */
std::string full_font = FioFindFullPath(BASE_DIR, font_name);
if (!full_font.empty()) {
path.reset(CFStringCreateWithCString(kCFAllocatorDefault, full_font.c_str(), kCFStringEncodingUTF8));
}
}
if (path) {
/* Try getting a font descriptor to see if the system can use it. */
CFAutoRelease<CFURLRef> url(CFURLCreateWithFileSystemPath(kCFAllocatorDefault, path.get(), kCFURLPOSIXPathStyle, false));
CFAutoRelease<CFArrayRef> descs(CTFontManagerCreateFontDescriptorsFromURL(url.get()));
if (descs && CFArrayGetCount(descs.get()) > 0) {
CTFontDescriptorRef font_ref = (CTFontDescriptorRef)CFArrayGetValueAtIndex(descs.get(), 0);
CFRetain(font_ref);
return font_ref;
}
}
return nullptr;
}
};
static CoreTextFontCacheFactory s_coretext_fontcache_Factory;

View File

@ -12,6 +12,7 @@ add_files(
add_files(
font_unix.cpp
font_unix.h
CONDITION Fontconfig_FOUND
)

View File

@ -11,17 +11,17 @@
#include "../../misc/autorelease.hpp"
#include "../../debug.h"
#include "../../fontdetection.h"
#include "../../fontcache.h"
#include "../../string_func.h"
#include "../../strings_func.h"
#include "font_unix.h"
#include <fontconfig/fontconfig.h>
#include "../../safeguards.h"
#include <ft2build.h>
#include FT_FREETYPE_H
#include "../../safeguards.h"
extern FT_Library _ft_library;
/**
@ -61,6 +61,12 @@ static std::tuple<std::string, std::string> SplitFontFamilyAndStyle(std::string_
return { std::string(font_name.substr(0, separator)), std::string(font_name.substr(begin)) };
}
/**
* Load a freetype font face with the given font name.
* @param font_name The name of the font to load.
* @param face The face that has been found.
* @return The error we encountered.
*/
FT_Error GetFontByFaceName(std::string_view font_name, FT_Face *face)
{
FT_Error err = FT_Err_Cannot_Open_Resource;
@ -117,7 +123,7 @@ FT_Error GetFontByFaceName(std::string_view font_name, FT_Face *face)
return err;
}
bool SetFallbackFont(FontCacheSettings *settings, const std::string &language_isocode, MissingGlyphSearcher *callback)
bool FontConfigFindFallbackFont(FontCacheSettings *settings, const std::string &language_isocode, MissingGlyphSearcher *callback)
{
bool ret = false;
@ -182,6 +188,6 @@ bool SetFallbackFont(FontCacheSettings *settings, const std::string &language_is
if (best_font == nullptr) return false;
callback->SetFontNames(settings, best_font, &best_index);
InitFontCache(callback->Monospace());
FontCache::LoadFontCaches(callback->Monospace() ? FontSizes{FS_MONO} : FONTSIZES_REQUIRED);
return true;
}

View File

@ -0,0 +1,26 @@
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file font_unix.h Functions related to detecting/finding the right font. */
#ifndef FONT_UNIX_H
#define FONT_UNIX_H
#ifdef WITH_FONTCONFIG
#include "../../fontcache.h"
#include <ft2build.h>
#include FT_FREETYPE_H
FT_Error GetFontByFaceName(std::string_view font_name, FT_Face *face);
bool FontConfigFindFallbackFont(FontCacheSettings *settings, const std::string &language_isocode, MissingGlyphSearcher *callback);
#endif /* WITH_FONTCONFIG */
#endif /* FONT_UNIX_H */

View File

@ -15,7 +15,6 @@
#include "../../fileio_func.h"
#include "../../fontcache.h"
#include "../../fontcache/truetypefontcache.h"
#include "../../fontdetection.h"
#include "../../library_loader.h"
#include "../../string_func.h"
#include "../../strings_func.h"
@ -85,32 +84,6 @@ static int CALLBACK EnumFontCallback(const ENUMLOGFONTEX *logfont, const NEWTEXT
return 0; // stop enumerating
}
bool SetFallbackFont(FontCacheSettings *settings, const std::string &language_isocode, MissingGlyphSearcher *callback)
{
Debug(fontcache, 1, "Trying fallback fonts");
EFCParam langInfo;
std::wstring lang = OTTD2FS(language_isocode.substr(0, language_isocode.find('_')));
if (GetLocaleInfoEx(lang.c_str(), LOCALE_FONTSIGNATURE, reinterpret_cast<LPWSTR>(&langInfo.locale), sizeof(langInfo.locale) / sizeof(wchar_t)) == 0) {
/* Invalid isocode or some other mysterious error, can't determine fallback font. */
Debug(fontcache, 1, "Can't get locale info for fallback font (isocode={})", language_isocode);
return false;
}
langInfo.settings = settings;
langInfo.callback = callback;
LOGFONT font;
/* Enumerate all fonts. */
font.lfCharSet = DEFAULT_CHARSET;
font.lfFaceName[0] = '\0';
font.lfPitchAndFamily = 0;
HDC dc = GetDC(nullptr);
int ret = EnumFontFamiliesEx(dc, &font, (FONTENUMPROC)&EnumFontCallback, (LPARAM)&langInfo, 0);
ReleaseDC(nullptr, dc);
return ret == 0;
}
/**
* Create a new Win32FontCache.
* @param fs The font size that is going to be cached.
@ -293,99 +266,134 @@ void Win32FontCache::ClearFontCache()
return allow_fallback && key >= SCC_SPRITE_START && key <= SCC_SPRITE_END ? this->parent->MapCharToGlyph(key) : 0;
}
class Win32FontCacheFactory : FontCacheFactory {
public:
Win32FontCacheFactory() : FontCacheFactory("win32", "Win32 font loader") {}
static bool TryLoadFontFromFile(const std::string &font_name, LOGFONT &logfont)
{
wchar_t fontPath[MAX_PATH] = {};
/**
* Loads the GDI font.
* If a GDI font description is present, e.g. from the automatic font
* fallback search, use it. Otherwise, try to resolve it by font name.
* @param fs The font size to load.
*/
std::unique_ptr<FontCache> LoadFont(FontSize fs, FontType fonttype) override
{
if (fonttype != FontType::TrueType) return nullptr;
/* See if this is an absolute path. */
if (FileExists(font_name)) {
convert_to_fs(font_name, fontPath);
} else {
/* Scan the search-paths to see if it can be found. */
std::string full_font = FioFindFullPath(BASE_DIR, font_name);
if (!full_font.empty()) {
convert_to_fs(font_name, fontPath);
FontCacheSubSetting *settings = GetFontCacheSubSetting(fs);
std::string font = GetFontCacheFontName(fs);
if (font.empty()) return nullptr;
LOGFONT logfont{};
logfont.lfPitchAndFamily = fs == FS_MONO ? FIXED_PITCH : VARIABLE_PITCH;
logfont.lfCharSet = DEFAULT_CHARSET;
logfont.lfOutPrecision = OUT_OUTLINE_PRECIS;
logfont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
if (settings->os_handle != nullptr) {
logfont = *(const LOGFONT *)settings->os_handle;
} else if (font.find('.') != std::string::npos) {
/* Might be a font file name, try load it. */
if (!TryLoadFontFromFile(font, logfont)) {
ShowInfo("Unable to load file '{}' for {} font, using default windows font selection instead", font, FontSizeToName(fs));
}
}
if (logfont.lfFaceName[0] == 0) {
logfont.lfWeight = StrContainsIgnoreCase(font, " bold") ? FW_BOLD : FW_NORMAL; // Poor man's way to allow selecting bold fonts.
convert_to_fs(font, logfont.lfFaceName);
}
return LoadWin32Font(fs, logfont, GetFontCacheFontSize(fs), font);
}
if (fontPath[0] != 0) {
if (AddFontResourceEx(fontPath, FR_PRIVATE, 0) != 0) {
/* Try a nice little undocumented function first for getting the internal font name.
* Some documentation is found at: http://www.undocprint.org/winspool/getfontresourceinfo */
static LibraryLoader _gdi32("gdi32.dll");
typedef BOOL(WINAPI *PFNGETFONTRESOURCEINFO)(LPCTSTR, LPDWORD, LPVOID, DWORD);
static PFNGETFONTRESOURCEINFO GetFontResourceInfo = _gdi32.GetFunction("GetFontResourceInfoW");
bool FindFallbackFont(FontCacheSettings *settings, const std::string &language_isocode, MissingGlyphSearcher *callback) override
{
Debug(fontcache, 1, "Trying fallback fonts");
EFCParam langInfo;
std::wstring lang = OTTD2FS(language_isocode.substr(0, language_isocode.find('_')));
if (GetLocaleInfoEx(lang.c_str(), LOCALE_FONTSIGNATURE, reinterpret_cast<LPWSTR>(&langInfo.locale), sizeof(langInfo.locale) / sizeof(wchar_t)) == 0) {
/* Invalid isocode or some other mysterious error, can't determine fallback font. */
Debug(fontcache, 1, "Can't get locale info for fallback font (isocode={})", language_isocode);
return false;
}
langInfo.settings = settings;
langInfo.callback = callback;
if (GetFontResourceInfo != nullptr) {
/* Try to query an array of LOGFONTs that describe the file. */
DWORD len = 0;
if (GetFontResourceInfo(fontPath, &len, nullptr, 2) && len >= sizeof(LOGFONT)) {
LOGFONT *buf = (LOGFONT *)new uint8_t[len];
if (GetFontResourceInfo(fontPath, &len, buf, 2)) {
logfont = *buf; // Just use first entry.
LOGFONT font;
/* Enumerate all fonts. */
font.lfCharSet = DEFAULT_CHARSET;
font.lfFaceName[0] = '\0';
font.lfPitchAndFamily = 0;
HDC dc = GetDC(nullptr);
int ret = EnumFontFamiliesEx(dc, &font, (FONTENUMPROC)&EnumFontCallback, (LPARAM)&langInfo, 0);
ReleaseDC(nullptr, dc);
return ret == 0;
}
private:
static std::unique_ptr<FontCache> LoadWin32Font(FontSize fs, const LOGFONT &logfont, uint size, std::string_view font_name)
{
HFONT font = CreateFontIndirect(&logfont);
if (font == nullptr) {
ShowInfo("Unable to use '{}' for {} font, Win32 reported error 0x{:X}, using sprite font instead", font_name, FontSizeToName(fs), GetLastError());
return nullptr;
}
DeleteObject(font);
return std::make_unique<Win32FontCache>(fs, logfont, size);
}
static bool TryLoadFontFromFile(const std::string &font_name, LOGFONT &logfont)
{
wchar_t fontPath[MAX_PATH] = {};
/* See if this is an absolute path. */
if (FileExists(font_name)) {
convert_to_fs(font_name, fontPath);
} else {
/* Scan the search-paths to see if it can be found. */
std::string full_font = FioFindFullPath(BASE_DIR, font_name);
if (!full_font.empty()) {
convert_to_fs(font_name, fontPath);
}
}
if (fontPath[0] != 0) {
if (AddFontResourceEx(fontPath, FR_PRIVATE, 0) != 0) {
/* Try a nice little undocumented function first for getting the internal font name.
* Some documentation is found at: http://www.undocprint.org/winspool/getfontresourceinfo */
static LibraryLoader _gdi32("gdi32.dll");
typedef BOOL(WINAPI *PFNGETFONTRESOURCEINFO)(LPCTSTR, LPDWORD, LPVOID, DWORD);
static PFNGETFONTRESOURCEINFO GetFontResourceInfo = _gdi32.GetFunction("GetFontResourceInfoW");
if (GetFontResourceInfo != nullptr) {
/* Try to query an array of LOGFONTs that describe the file. */
DWORD len = 0;
if (GetFontResourceInfo(fontPath, &len, nullptr, 2) && len >= sizeof(LOGFONT)) {
LOGFONT *buf = (LOGFONT *)new uint8_t[len];
if (GetFontResourceInfo(fontPath, &len, buf, 2)) {
logfont = *buf; // Just use first entry.
}
delete[](uint8_t *)buf;
}
delete[](uint8_t *)buf;
}
/* No dice yet. Use the file name as the font face name, hoping it matches. */
if (logfont.lfFaceName[0] == 0) {
wchar_t fname[_MAX_FNAME];
_wsplitpath(fontPath, nullptr, nullptr, fname, nullptr);
wcsncpy_s(logfont.lfFaceName, lengthof(logfont.lfFaceName), fname, _TRUNCATE);
logfont.lfWeight = StrContainsIgnoreCase(font_name, " bold") || StrContainsIgnoreCase(font_name, "-bold") ? FW_BOLD : FW_NORMAL; // Poor man's way to allow selecting bold fonts.
}
}
/* No dice yet. Use the file name as the font face name, hoping it matches. */
if (logfont.lfFaceName[0] == 0) {
wchar_t fname[_MAX_FNAME];
_wsplitpath(fontPath, nullptr, nullptr, fname, nullptr);
wcsncpy_s(logfont.lfFaceName, lengthof(logfont.lfFaceName), fname, _TRUNCATE);
logfont.lfWeight = StrContainsIgnoreCase(font_name, " bold") || StrContainsIgnoreCase(font_name, "-bold") ? FW_BOLD : FW_NORMAL; // Poor man's way to allow selecting bold fonts.
}
}
return logfont.lfFaceName[0] != 0;
}
};
return logfont.lfFaceName[0] != 0;
}
static void LoadWin32Font(FontSize fs, const LOGFONT &logfont, uint size, std::string_view font_name)
{
HFONT font = CreateFontIndirect(&logfont);
if (font == nullptr) {
ShowInfo("Unable to use '{}' for {} font, Win32 reported error 0x{:X}, using sprite font instead", font_name, FontSizeToName(fs), GetLastError());
return;
}
DeleteObject(font);
new Win32FontCache(fs, logfont, size);
}
/**
* Loads the GDI font.
* If a GDI font description is present, e.g. from the automatic font
* fallback search, use it. Otherwise, try to resolve it by font name.
* @param fs The font size to load.
*/
void LoadWin32Font(FontSize fs)
{
FontCacheSubSetting *settings = GetFontCacheSubSetting(fs);
std::string font = GetFontCacheFontName(fs);
if (font.empty()) return;
LOGFONT logfont{};
logfont.lfPitchAndFamily = fs == FS_MONO ? FIXED_PITCH : VARIABLE_PITCH;
logfont.lfCharSet = DEFAULT_CHARSET;
logfont.lfOutPrecision = OUT_OUTLINE_PRECIS;
logfont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
if (settings->os_handle != nullptr) {
logfont = *(const LOGFONT *)settings->os_handle;
} else if (font.find('.') != std::string::npos) {
/* Might be a font file name, try load it. */
if (!TryLoadFontFromFile(font, logfont)) {
ShowInfo("Unable to load file '{}' for {} font, using default windows font selection instead", font, FontSizeToName(fs));
}
}
if (logfont.lfFaceName[0] == 0) {
logfont.lfWeight = StrContainsIgnoreCase(font, " bold") ? FW_BOLD : FW_NORMAL; // Poor man's way to allow selecting bold fonts.
convert_to_fs(font, logfont.lfFaceName);
}
LoadWin32Font(fs, logfont, GetFontCacheFontSize(fs), font);
}
static Win32FontCacheFactory s_win32_fontcache_factory;

View File

@ -1036,9 +1036,8 @@ struct GameOptionsWindow : Window {
this->SetWidgetDisabledState(WID_GO_GUI_FONT_AA, _fcsettings.prefer_sprite);
this->SetDirty();
InitFontCache(false);
InitFontCache(true);
ClearFontCache();
FontCache::LoadFontCaches(FONTSIZES_ALL);
FontCache::ClearFontCaches(FONTSIZES_ALL);
CheckForMissingGlyphs();
SetupWidgetDimensions();
UpdateAllVirtCoords();
@ -1051,7 +1050,7 @@ struct GameOptionsWindow : Window {
this->SetWidgetLoweredState(WID_GO_GUI_FONT_AA, _fcsettings.global_aa);
MarkWholeScreenDirty();
ClearFontCache();
FontCache::ClearFontCaches(FONTSIZES_ALL);
break;
#endif /* HAS_TRUETYPE_FONT */

View File

@ -17,7 +17,6 @@
#include "newgrf_text.h"
#include "fileio_func.h"
#include "signs_base.h"
#include "fontdetection.h"
#include "error.h"
#include "error_func.h"
#include "strings_func.h"
@ -2278,7 +2277,7 @@ std::string_view GetCurrentLanguageIsoCode()
*/
bool MissingGlyphSearcher::FindMissingGlyphs()
{
InitFontCache(this->Monospace());
FontCache::LoadFontCaches(this->Monospace() ? FontSizes{FS_MONO} : FONTSIZES_REQUIRED);
this->Reset();
for (auto text = this->NextString(); text.has_value(); text = this->NextString()) {
@ -2376,7 +2375,7 @@ void CheckForMissingGlyphs(bool base_font, MissingGlyphSearcher *searcher)
_fcsettings.mono.os_handle = nullptr;
_fcsettings.medium.os_handle = nullptr;
bad_font = !SetFallbackFont(&_fcsettings, _langpack.langpack->isocode, searcher);
bad_font = !FontProviderManager::FindFallbackFont(&_fcsettings, _langpack.langpack->isocode, searcher);
_fcsettings = std::move(backup);
@ -2395,7 +2394,7 @@ void CheckForMissingGlyphs(bool base_font, MissingGlyphSearcher *searcher)
/* 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 */
InitFontCache(searcher->Monospace());
FontCache::LoadFontCaches(searcher->Monospace() ? FontSizes{FS_MONO} : FONTSIZES_REQUIRED);
}
}
#endif
@ -2412,12 +2411,12 @@ void CheckForMissingGlyphs(bool base_font, MissingGlyphSearcher *searcher)
ShowErrorMessage(GetEncodedString(STR_JUST_RAW_STRING, std::move(err_str)), {}, WL_WARNING);
/* Reset the font width */
LoadStringWidthTable(searcher->Monospace());
LoadStringWidthTable(searcher->Monospace() ? FontSizes{FS_MONO} : FONTSIZES_REQUIRED);
return;
}
/* Update the font with cache */
LoadStringWidthTable(searcher->Monospace());
LoadStringWidthTable(searcher->Monospace() ? FontSizes{FS_MONO} : FONTSIZES_REQUIRED);
#if !(defined(WITH_ICU_I18N) && defined(WITH_HARFBUZZ)) && !defined(WITH_UNISCRIBE) && !defined(WITH_COCOA)
/*

View File

@ -21,8 +21,6 @@ public:
this->height = FontCache::GetDefaultFontHeight(this->fs);
}
void SetUnicodeGlyph(char32_t, SpriteID) override {}
void InitializeUnicodeGlyphMap() override {}
void ClearFontCache() override {}
const Sprite *GetGlyph(GlyphID) override { return nullptr; }
uint GetGlyphWidth(GlyphID) override { return this->height / 2; }
@ -34,7 +32,8 @@ public:
static void InitializeFontCaches()
{
for (FontSize fs = FS_BEGIN; fs != FS_END; fs++) {
if (FontCache::caches[fs] == nullptr) new MockFontCache(fs); /* FontCache inserts itself into to the cache. */
if (FontCache::Get(fs) != nullptr) continue;
FontCache::Register(std::make_unique<MockFontCache>(fs));
}
}
};