diff --git a/CREDITS.md b/CREDITS.md index 8826c9dcfe..1988223bf9 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -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! diff --git a/media/baseset/CMakeLists.txt b/media/baseset/CMakeLists.txt index d3c5f87337..b92eb0f648 100644 --- a/media/baseset/CMakeLists.txt +++ b/media/baseset/CMakeLists.txt @@ -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 diff --git a/media/baseset/OpenTTD-Mono.ttf b/media/baseset/OpenTTD-Mono.ttf new file mode 100644 index 0000000000..678050e512 Binary files /dev/null and b/media/baseset/OpenTTD-Mono.ttf differ diff --git a/media/baseset/OpenTTD-Sans.ttf b/media/baseset/OpenTTD-Sans.ttf new file mode 100644 index 0000000000..3c97a65e14 Binary files /dev/null and b/media/baseset/OpenTTD-Sans.ttf differ diff --git a/media/baseset/OpenTTD-Serif.ttf b/media/baseset/OpenTTD-Serif.ttf new file mode 100644 index 0000000000..5a139b99f1 Binary files /dev/null and b/media/baseset/OpenTTD-Serif.ttf differ diff --git a/media/baseset/OpenTTD-Small.ttf b/media/baseset/OpenTTD-Small.ttf new file mode 100644 index 0000000000..5e702c1850 Binary files /dev/null and b/media/baseset/OpenTTD-Small.ttf differ diff --git a/media/baseset/OpenTTD-font.md b/media/baseset/OpenTTD-font.md new file mode 100644 index 0000000000..04cc878cf3 --- /dev/null +++ b/media/baseset/OpenTTD-font.md @@ -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. diff --git a/src/fontcache.cpp b/src/fontcache.cpp index 0f94fd2c82..42643bbff7 100644 --- a/src/fontcache.cpp +++ b/src/fontcache.cpp @@ -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 + } } } diff --git a/src/fontcache/freetypefontcache.cpp b/src/fontcache/freetypefontcache.cpp index eca647d9c8..bcbfbc2a52 100644 --- a/src/fontcache/freetypefontcache.cpp +++ b/src/fontcache/freetypefontcache.cpp @@ -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); } +} - 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; + } - ShowInfo("Unable to use '{}' for {} font, FreeType reported error 0x{:X}, using sprite font instead", font_name, FontSizeToName(fs), error); - return; + Debug(fontcache, 2, "Initialized"); + } -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); + } } diff --git a/src/os/macosx/font_osx.cpp b/src/os/macosx/font_osx.cpp index d76fb0ef5c..2b5f0e14b1 100644 --- a/src/os/macosx/font_osx.cpp +++ b/src/os/macosx/font_osx.cpp @@ -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 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 url(CFURLCreateWithFileSystemPath(kCFAllocatorDefault, path.get(), kCFURLPOSIXPathStyle, false)); + CFAutoRelease 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 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 url(CFURLCreateWithFileSystemPath(kCFAllocatorDefault, path.get(), kCFURLPOSIXPathStyle, false)); - CFAutoRelease 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 font_ref{LoadFontFromFile(file_name)}; + if (font_ref) { + new CoreTextFontCache(fs, std::move(font_ref), size); + } +} diff --git a/src/os/macosx/font_osx.h b/src/os/macosx/font_osx.h index 56d488771a..ca2ac1d5e9 100644 --- a/src/os/macosx/font_osx.h +++ b/src/os/macosx/font_osx.h @@ -36,5 +36,6 @@ public: }; void LoadCoreTextFont(FontSize fs); +void LoadCoreTextFont(FontSize fs, const std::string &file_name, uint size); #endif /* FONT_OSX_H */ diff --git a/src/os/windows/font_win32.cpp b/src/os/windows/font_win32.cpp index 3602c11876..1dc1977b37 100644 --- a/src/os/windows/font_win32.cpp +++ b/src/os/windows/font_win32.cpp @@ -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); + 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; - new Win32FontCache(fs, logfont, settings->size); + if (TryLoadFontFromFile(file_name, logfont)) { + LoadWin32Font(fs, logfont, size, file_name.c_str()); + } } diff --git a/src/os/windows/font_win32.h b/src/os/windows/font_win32.h index 48459543fd..bdb2cc9a9b 100644 --- a/src/os/windows/font_win32.h +++ b/src/os/windows/font_win32.h @@ -41,5 +41,6 @@ public: }; void LoadWin32Font(FontSize fs); +void LoadWin32Font(FontSize fs, const std::string &file_name, uint size); #endif /* FONT_WIN32_H */