Feature: Scalable OpenTTD TrueType font made by Zephyris.

This uses the normal variant for the small font, not the all-caps version.
This commit is contained in:
Michael Lutz 2023-12-16 01:03:28 +01:00
parent 6e766a2e81
commit 36cf6b1d13
13 changed files with 261 additions and 114 deletions

View File

@ -56,6 +56,7 @@
- George - Canal/Lock graphics
- Andrew Parkhouse (andythenorth) - River graphics
- David Dallaston (Pikka) - Tram tracks
- Richard Wheeler (zephyris) - OpenTTD TrueType font
- All Translators - For their support to make OpenTTD a truly international game
- Bug Reporters - Thanks for all bug reports
- Chris Sawyer - For an amazing game!

View File

@ -18,6 +18,10 @@ set(BASESET_OTHER_SOURCE_FILES
${CMAKE_CURRENT_SOURCE_DIR}/opntitle.dat
${CMAKE_CURRENT_SOURCE_DIR}/orig_extra.grf
${CMAKE_CURRENT_SOURCE_DIR}/../openttd.32.bmp
${CMAKE_CURRENT_SOURCE_DIR}/OpenTTD-Sans.ttf
${CMAKE_CURRENT_SOURCE_DIR}/OpenTTD-Serif.ttf
${CMAKE_CURRENT_SOURCE_DIR}/OpenTTD-Small.ttf
${CMAKE_CURRENT_SOURCE_DIR}/OpenTTD-Mono.ttf
)
# Done by the subdirectories, if nforenum / grfcodec is installed

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,6 @@
# OpenTTD TrueType font
The OpenTTD TrueType font was created by Zephyris and is maintained on [Github](https://github.com/zephyris/openttd-ttf).
It is licensed under GPL-2.0.
The currently included files correspond to release v0.3.

View File

@ -18,6 +18,7 @@
#include "strings_func.h"
#include "viewport_func.h"
#include "window_func.h"
#include "fileio_func.h"
#include "safeguards.h"
@ -143,13 +144,52 @@ void SetFont(FontSize fontsize, const std::string &font, uint size, bool aa)
#ifdef WITH_FREETYPE
extern void LoadFreeTypeFont(FontSize fs);
extern void LoadFreeTypeFont(FontSize fs, const std::string &file_name, uint size);
extern void UninitFreeType();
#elif defined(_WIN32)
extern void LoadWin32Font(FontSize fs);
extern void LoadWin32Font(FontSize fs, const std::string &file_name, uint size);
#elif defined(WITH_COCOA)
extern void LoadCoreTextFont(FontSize fs);
extern void LoadCoreTextFont(FontSize fs, const std::string &file_name, uint size);
#endif
static void TryLoadDefaultTrueTypeFont([[maybe_unused]] FontSize fs)
{
#if defined(WITH_FREETYPE) || defined(_WIN32) || defined(WITH_COCOA)
std::string font_name{};
switch (fs) {
case FS_NORMAL:
font_name = "OpenTTD-Sans.ttf";
break;
case FS_SMALL:
font_name = "OpenTTD-Small.ttf";
break;
case FS_LARGE:
font_name = "OpenTTD-Serif.ttf";
break;
case FS_MONO:
font_name = "OpenTTD-Mono.ttf";
break;
default: NOT_REACHED();
}
/* Find font file. */
std::string full_font = FioFindFullPath(BASESET_DIR, font_name);
if (!full_font.empty()) {
int size = FontCache::GetDefaultFontHeight(fs);
#ifdef WITH_FREETYPE
LoadFreeTypeFont(fs, full_font, size);
#elif defined(_WIN32)
LoadWin32Font(fs, full_font, size);
#elif defined(WITH_COCOA)
LoadCoreTextFont(fs, full_font, size);
#endif
}
#endif /* defined(WITH_FREETYPE) || defined(_WIN32) || defined(WITH_COCOA) */
}
/**
* (Re)initialize the font cache related things, i.e. load the non-sprite fonts.
* @param monospace Whether to initialise the monospace or regular fonts.
@ -164,13 +204,17 @@ void InitFontCache(bool monospace)
FontCache *fc = FontCache::Get(fs);
if (fc->HasParent()) delete fc;
if (GetFontCacheSubSetting(fs)->font.empty()) {
TryLoadDefaultTrueTypeFont(fs);
} else {
#ifdef WITH_FREETYPE
LoadFreeTypeFont(fs);
LoadFreeTypeFont(fs);
#elif defined(_WIN32)
LoadWin32Font(fs);
LoadWin32Font(fs);
#elif defined(WITH_COCOA)
LoadCoreTextFont(fs);
LoadCoreTextFont(fs);
#endif
}
}
}

View File

@ -115,6 +115,42 @@ void FreeTypeFontCache::SetFontSize(FontSize, FT_Face, int pixels)
}
}
static FT_Error LoadFont(FontSize fs, FT_Face face, const char *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,
@ -157,40 +193,40 @@ void LoadFreeTypeFont(FontSize fs)
if (error != FT_Err_Ok) error = GetFontByFaceName(font_name, &face);
if (error == FT_Err_Ok) {
Debug(fontcache, 2, "Requested '{}', using '{} {}'", font_name, face->family_name, face->style_name);
/* Attempt to select the unicode character map */
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;
}
error = LoadFont(fs, face, font_name, settings->size);
if (error != FT_Err_Ok) {
ShowInfo("Unable to use '{}' for {} font, FreeType reported error 0x{:X}, using sprite font instead", font_name, FontSizeToName(fs), error);
}
} else {
FT_Done_Face(face);
}
}
/**
* Load a TrueType font from a file.
* @param fs The font size to load.
* @param file_name Path to the font file.
* @param size Requested font size.
*/
void LoadFreeTypeFont(FontSize fs, const std::string &file_name, uint size)
{
if (_library == nullptr) {
if (FT_Init_FreeType(&_library) != FT_Err_Ok) {
ShowInfo("Unable to initialize FreeType, using sprite fonts instead");
return;
}
Debug(fontcache, 2, "Initialized");
}
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;
found_face:
new FreeTypeFontCache(fs, face, settings->size);
FT_Face face = nullptr;
int32_t index = 0;
FT_Error error = FT_New_Face(_library, file_name.c_str(), index, &face);
if (error == FT_Err_Ok) {
LoadFont(fs, face, file_name.c_str(), size);
} else {
FT_Done_Face(face);
}
}

View File

@ -298,6 +298,40 @@ const Sprite *CoreTextFontCache::InternalGetGlyph(GlyphID key, bool use_aa)
return new_glyph.sprite;
}
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;
}
/**
* Loads the TrueType font.
* If a CoreText font description is present, e.g. from the automatic font
@ -318,33 +352,9 @@ void LoadCoreTextFont(FontSize fs)
}
if (!font_ref && MacOSVersionIsAtLeast(10, 6, 0)) {
/* 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(settings->font)) {
path.reset(CFStringCreateWithCString(kCFAllocatorDefault, settings->font.c_str(), kCFStringEncodingUTF8));
} else {
/* Scan the search-paths to see if it can be found. */
std::string full_font = FioFindFullPath(BASE_DIR, settings->font);
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) {
font_ref.reset((CTFontDescriptorRef)CFArrayGetValueAtIndex(descs.get(), 0));
CFRetain(font_ref.get());
} else {
ShowInfo("Unable to load file '{}' for {} font, using default OS font selection instead", settings->font, FontSizeToName(fs));
}
}
/* Might be a font file name, try load it. */
font_ref.reset(LoadFontFromFile(settings->font));
if (!font_ref) ShowInfo("Unable to load file '{}' for {} font, using default OS font selection instead", settings->font, FontSizeToName(fs));
}
if (!font_ref) {
@ -372,3 +382,17 @@ void LoadCoreTextFont(FontSize fs)
new CoreTextFontCache(fs, std::move(font_ref), settings->size);
}
/**
* Load a TrueType font from a file.
* @param fs The font size to load.
* @param file_name Path to the font file.
* @param size Requested font size.
*/
void LoadCoreTextFont(FontSize fs, const std::string &file_name, uint size)
{
CFAutoRelease<CTFontDescriptorRef> font_ref{LoadFontFromFile(file_name)};
if (font_ref) {
new CoreTextFontCache(fs, std::move(font_ref), size);
}
}

View File

@ -36,5 +36,6 @@ public:
};
void LoadCoreTextFont(FontSize fs);
void LoadCoreTextFont(FontSize fs, const std::string &file_name, uint size);
#endif /* FONT_OSX_H */

View File

@ -317,6 +317,66 @@ void Win32FontCache::ClearFontCache()
}
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, lengthof(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, lengthof(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 byte[len];
if (GetFontResourceInfo(fontPath, &len, buf, 2)) {
logfont = *buf; // Just use first entry.
}
delete[](byte *)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 = strcasestr(font_name.c_str(), " bold") != nullptr || strcasestr(font_name.c_str(), "-bold") != nullptr ? FW_BOLD : FW_NORMAL; // Poor man's way to allow selecting bold fonts.
}
}
}
return logfont.lfFaceName[0] != 0;
}
static void LoadWin32Font(FontSize fs, const LOGFONT &logfont, uint size, const char *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
@ -341,51 +401,8 @@ void LoadWin32Font(FontSize fs)
logfont = *(const LOGFONT *)settings->os_handle;
} else if (strchr(font_name, '.') != nullptr) {
/* Might be a font file name, try load it. */
wchar_t fontPath[MAX_PATH] = {};
/* See if this is an absolute path. */
if (FileExists(settings->font)) {
convert_to_fs(font_name, fontPath, lengthof(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, lengthof(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 byte[len];
if (GetFontResourceInfo(fontPath, &len, buf, 2)) {
logfont = *buf; // Just use first entry.
}
delete[] (byte *)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 = strcasestr(font_name, " bold") != nullptr || strcasestr(font_name, "-bold") != nullptr ? FW_BOLD : FW_NORMAL; // Poor man's way to allow selecting bold fonts.
}
} else {
ShowInfo("Unable to load file '{}' for {} font, using default windows font selection instead", font_name, FontSizeToName(fs));
}
if (!TryLoadFontFromFile(settings->font, logfont)) {
ShowInfo("Unable to load file '{}' for {} font, using default windows font selection instead", font_name, FontSizeToName(fs));
}
}
@ -394,12 +411,25 @@ void LoadWin32Font(FontSize fs)
convert_to_fs(font_name, logfont.lfFaceName, lengthof(logfont.lfFaceName));
}
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, settings->size);
LoadWin32Font(fs, logfont, settings->size, font_name);
}
/**
* Load a TrueType font from a file.
* @param fs The font size to load.
* @param file_name Path to the font file.
* @param size Requested font size.
*/
void LoadWin32Font(FontSize fs, const std::string &file_name, uint size)
{
LOGFONT logfont;
MemSetT(&logfont, 0);
logfont.lfPitchAndFamily = fs == FS_MONO ? FIXED_PITCH : VARIABLE_PITCH;
logfont.lfCharSet = DEFAULT_CHARSET;
logfont.lfOutPrecision = OUT_OUTLINE_PRECIS;
logfont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
if (TryLoadFontFromFile(file_name, logfont)) {
LoadWin32Font(fs, logfont, size, file_name.c_str());
}
}

View File

@ -41,5 +41,6 @@ public:
};
void LoadWin32Font(FontSize fs);
void LoadWin32Font(FontSize fs, const std::string &file_name, uint size);
#endif /* FONT_WIN32_H */