From 169d6c412964f8c4cc4a2aaff20420255b7ddd9c Mon Sep 17 00:00:00 2001 From: ohyzha Date: Fri, 17 Jan 2025 13:11:04 +0200 Subject: [PATCH 01/15] atlas factory Windows implementation --- openVulkanoCpp/Host/Linux/SystemInfo.cpp | 6 ++ openVulkanoCpp/Host/MacOS/SystemInfo.mm | 7 +- openVulkanoCpp/Host/SystemInfo.hpp | 1 + openVulkanoCpp/Host/Windows/SystemInfo.cpp | 57 +++++++++++++ openVulkanoCpp/Host/iOS/SystemInfo.mm | 7 +- openVulkanoCpp/Scene/Text/FontAtlas.hpp | 2 + .../Scene/Text/FontAtlasFactory.cpp | 79 +++++++++++++++++++ .../Scene/Text/FontAtlasFactory.hpp | 69 ++++++++++++++++ tests/Host/Windows/SystemFontsSearchTests.cpp | 34 ++++++++ 9 files changed, 260 insertions(+), 2 deletions(-) create mode 100644 openVulkanoCpp/Scene/Text/FontAtlasFactory.cpp create mode 100644 openVulkanoCpp/Scene/Text/FontAtlasFactory.hpp create mode 100644 tests/Host/Windows/SystemFontsSearchTests.cpp diff --git a/openVulkanoCpp/Host/Linux/SystemInfo.cpp b/openVulkanoCpp/Host/Linux/SystemInfo.cpp index 19fe0cf..896a9c8 100644 --- a/openVulkanoCpp/Host/Linux/SystemInfo.cpp +++ b/openVulkanoCpp/Host/Linux/SystemInfo.cpp @@ -394,4 +394,10 @@ namespace OpenVulkano { return InterfaceOrientation::Landscape; // TODO? } + + std::string SystemInfo::GetSystemFontPath(const std::string& fontName) + { + return ""; + } + } \ No newline at end of file diff --git a/openVulkanoCpp/Host/MacOS/SystemInfo.mm b/openVulkanoCpp/Host/MacOS/SystemInfo.mm index ca295f6..db95584 100644 --- a/openVulkanoCpp/Host/MacOS/SystemInfo.mm +++ b/openVulkanoCpp/Host/MacOS/SystemInfo.mm @@ -111,7 +111,7 @@ namespace OpenVulkano { NSOperatingSystemVersion sysVersion = [NSProcessInfo processInfo].operatingSystemVersion; osVersion = { static_cast(sysVersion.majorVersion), static_cast(sysVersion.minorVersion), - static_cast(sysVersion.patchVersion), 0 }; + static_cast(sysVersion.patchVersion), 0 }; } return osVersion; } @@ -202,4 +202,9 @@ namespace OpenVulkano { return InterfaceOrientation::Landscape; //TODO? } + + std::string SystemInfo::GetSystemFontPath(const std::string& fontName) + { + return ""; + } } diff --git a/openVulkanoCpp/Host/SystemInfo.hpp b/openVulkanoCpp/Host/SystemInfo.hpp index 7ab324c..f3a0b3e 100644 --- a/openVulkanoCpp/Host/SystemInfo.hpp +++ b/openVulkanoCpp/Host/SystemInfo.hpp @@ -69,6 +69,7 @@ namespace OpenVulkano static DeviceOrientation GetDeviceOrientation(); static void EnableDeviceOrientationEvents(); static InterfaceOrientation GetInterfaceOrientation(); + static std::string GetSystemFontPath(const std::string& fontName); static Event<> OnLowPowerModeChanged; static Event<> OnBatteryStateChanged; diff --git a/openVulkanoCpp/Host/Windows/SystemInfo.cpp b/openVulkanoCpp/Host/Windows/SystemInfo.cpp index bea3c6b..3366009 100644 --- a/openVulkanoCpp/Host/Windows/SystemInfo.cpp +++ b/openVulkanoCpp/Host/Windows/SystemInfo.cpp @@ -19,6 +19,9 @@ #include #include #include +#include +#include +#include // NOTE(vb): Windows defines macros like GetUserName that are used to automatically select the appropriate function version (GetUserNameA for ANSI and GetUserNameW for Unicode) // based on whether the _UNICODE macro is defined, so we manually undefine these macros to avoid naming collisions. @@ -28,6 +31,8 @@ #pragma comment(lib, "PowrProf.lib") #pragma comment(lib, "wbemuuid.lib") +#define QFR_DESCRIPTION 1 + namespace OpenVulkano { namespace @@ -585,4 +590,56 @@ namespace OpenVulkano return InterfaceOrientation::Landscape; } } + + std::string SystemInfo::GetSystemFontPath(const std::string& fontName) + { + // font name -> filename + static std::map fontFileMapping; + if (fontFileMapping.empty()) + { + // thank you Microsoft for function that is not even documented, but exists and it's the only function that + // can return real font name from font filename (e.g. font filename is times.ttf that corresponds to Times New Roman) + int(WINAPI * GetFontResourceInfoW)(wchar_t*, unsigned long*, void*, unsigned long); + *(FARPROC*) &GetFontResourceInfoW = GetProcAddress(GetModuleHandleA("gdi32"), "GetFontResourceInfoW"); + + std::wstring winDir; + winDir.resize(MAX_PATH); + UINT len = GetWindowsDirectoryW(winDir.data(), MAX_PATH); + winDir.resize(len); + std::filesystem::path fontsDir = std::filesystem::path(winDir) / "Fonts"; + for (const auto& fontFilename : std::filesystem::directory_iterator(fontsDir)) + { + unsigned long size = 0; + std::wstring ws = fontFilename.path().wstring(); + if (!GetFontResourceInfoW(ws.data(), &size, NULL, QFR_DESCRIPTION)) + { + continue; + } + std::wstring fontName; + fontName.resize(size); + if (GetFontResourceInfoW(ws.data(), &size, fontName.data(), QFR_DESCRIPTION)) + { + // remove null-terminated characters since size is always bigger than needed + std::string fontNameCropped(fontName.begin(), fontName.end()); + size_t realSize = 0; + for (; realSize < fontNameCropped.size(); realSize++) + { + if (fontNameCropped[realSize] == '\0') + { + break; + } + } + fontNameCropped.resize(realSize); + fontFileMapping[std::move(fontNameCropped)] = fontFilename.path().string(); + } + } + } + + // maybe check everything in lower case ? so that we can use Arial/arial as input parameter + if (fontFileMapping.contains(fontName)) + { + return fontFileMapping.at(fontName); + } + return ""; + } } \ No newline at end of file diff --git a/openVulkanoCpp/Host/iOS/SystemInfo.mm b/openVulkanoCpp/Host/iOS/SystemInfo.mm index 3182ba2..17b7855 100644 --- a/openVulkanoCpp/Host/iOS/SystemInfo.mm +++ b/openVulkanoCpp/Host/iOS/SystemInfo.mm @@ -114,7 +114,7 @@ namespace OpenVulkano { NSOperatingSystemVersion osVersion = [NSProcessInfo processInfo].operatingSystemVersion; osv = { static_cast(osVersion.majorVersion), static_cast(osVersion.minorVersion), - static_cast(osVersion.patchVersion), 0 }; + static_cast(osVersion.patchVersion), 0 }; } return osv; } @@ -288,4 +288,9 @@ namespace OpenVulkano } return InterfaceOrientation::Landscape; } + + std::string SystemInfo::GetSystemFontPath(const std::string& fontName) + { + return ""; + } } diff --git a/openVulkanoCpp/Scene/Text/FontAtlas.hpp b/openVulkanoCpp/Scene/Text/FontAtlas.hpp index 9f04be8..5703512 100644 --- a/openVulkanoCpp/Scene/Text/FontAtlas.hpp +++ b/openVulkanoCpp/Scene/Text/FontAtlas.hpp @@ -45,6 +45,8 @@ namespace OpenVulkano::Scene void DeserializeMetadata(const std::span& data); public: + using Ptr = std::shared_ptr; + FontAtlas() = default; FontAtlas(const Math::Vector2ui textureResolution, const double lineHeight, const FontAtlasType atlasType, DataFormat dataFormat) diff --git a/openVulkanoCpp/Scene/Text/FontAtlasFactory.cpp b/openVulkanoCpp/Scene/Text/FontAtlasFactory.cpp new file mode 100644 index 0000000..fd0427d --- /dev/null +++ b/openVulkanoCpp/Scene/Text/FontAtlasFactory.cpp @@ -0,0 +1,79 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include "FontAtlasFactory.hpp" +#include "Scene/SdfFontAtlasGenerator.hpp" +#include "Scene/BitmapFontAtlasGenerator.hpp" +#include "Host/SystemInfo.hpp" + +namespace OpenVulkano::Scene +{ + FontAtlas::Ptr FontAtlasFactory::GetFontAtlasScalable(const std::string& fontIdentifier, + const std::set& charset, bool msdf) const + { + const std::string fileName = FindFont(fontIdentifier); + if (fileName.empty()) + { + return nullptr; + } + + const std::set& setRef = (charset.empty() ? FontAtlasGeneratorBase::LoadAllGlyphs(fontIdentifier) : charset); + FontIdentifier id(fontIdentifier, setRef, SubpixelLayout::UNKNOWN, 0, msdf); + if (m_atlasesCache.contains(id)) + { + return m_atlasesCache.at(id); + } + + if (msdf) + { + MsdfFontAtlasGenerator msdfGen; + msdfGen.GenerateAtlas(fileName, setRef); + m_atlasesCache[id] = msdfGen.GetAtlas(); + return m_atlasesCache.at(id); + } + SdfFontAtlasGenerator sdfGen; + sdfGen.GenerateAtlas(fileName, setRef); + m_atlasesCache[id] = sdfGen.GetAtlas(); + return m_atlasesCache.at(id); + } + + FontAtlas::Ptr FontAtlasFactory::GetFontAtlas(const std::string& fontIdentifier, float ptSize, + const std::set& charset, + std::optional subpixelLayout) const + { + const std::string fileName = FindFont(fontIdentifier); + if (fileName.empty()) + { + return nullptr; + } + + const std::set& setRef = (charset.empty() ? FontAtlasGeneratorBase::LoadAllGlyphs(fontIdentifier) : charset); + FontIdentifier id(fontIdentifier, setRef, subpixelLayout.value_or(SubpixelLayout::UNKNOWN), ptSize, false); + if (m_atlasesCache.contains(id)) + { + return m_atlasesCache.at(id); + } + + FontPixelSizeConfig cfg(ptSize); + BitmapFontAtlasGenerator bitmapGen(cfg, subpixelLayout); + bitmapGen.GenerateAtlas(fileName, setRef); + m_atlasesCache[id] = bitmapGen.GetAtlas(); + return m_atlasesCache.at(id); + } + + std::string FontAtlasFactory::FindFont(const std::string& fontIdentifier) const + { + if (!std::filesystem::exists(fontIdentifier)) + { + if (!m_allowSystemFonts) + { + return ""; + } + return SystemInfo::GetSystemFontPath(fontIdentifier); + } + return fontIdentifier; + } +} diff --git a/openVulkanoCpp/Scene/Text/FontAtlasFactory.hpp b/openVulkanoCpp/Scene/Text/FontAtlasFactory.hpp new file mode 100644 index 0000000..31ed708 --- /dev/null +++ b/openVulkanoCpp/Scene/Text/FontAtlasFactory.hpp @@ -0,0 +1,69 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include "FontAtlas.hpp" +#include "Scene/SubpixelLayout.hpp" + +namespace OpenVulkano::Scene +{ + class FontAtlasFactory final + { + struct FontIdentifier + { + FontIdentifier(const std::string& font_, const std::set& charset, SubpixelLayout subpixelLayout_, + float ptSize_, bool msdf_) + : font(font_), subpixelLayout(subpixelLayout_), ptSize(ptSize_), msdf(msdf_) + { + std::for_each(charset.begin(), charset.end(), [&](uint32_t c) { charsetHash ^= c; }); + } + + std::string font; + size_t charsetHash = 0; + SubpixelLayout subpixelLayout = SubpixelLayout::UNKNOWN; + float ptSize = 0; + bool msdf = true; + + bool operator<(const FontIdentifier& other) const + { + if (font != other.font) + { + return font < other.font; + } + if (charsetHash != other.charsetHash) + { + return charsetHash < other.charsetHash; + } + if (subpixelLayout != other.subpixelLayout) + { + return subpixelLayout < other.subpixelLayout; + } + if (ptSize != other.ptSize) + { + return ptSize < other.ptSize; + } + return msdf < other.msdf; + } + + }; + public: + FontAtlasFactory(bool allowSystemFonts = true) : m_allowSystemFonts(allowSystemFonts) {} + [[nodiscard]] FontAtlas::Ptr GetFontAtlasScalable(const std::string& fontIdentifier, + const std::set& charset = {}, + bool msdf = true) const; + [[nodiscard]] FontAtlas::Ptr GetFontAtlas(const std::string& fontIdentifier, float ptSize, + const std::set& charset = {}, + std::optional subpixelLayout = std::nullopt) const; + + private: + std::string FindFont(const std::string& fontFile) const; + + private: + bool m_allowSystemFonts; + mutable std::map m_atlasesCache; + }; +} diff --git a/tests/Host/Windows/SystemFontsSearchTests.cpp b/tests/Host/Windows/SystemFontsSearchTests.cpp new file mode 100644 index 0000000..4fdf919 --- /dev/null +++ b/tests/Host/Windows/SystemFontsSearchTests.cpp @@ -0,0 +1,34 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include +#include "Base/Logger.hpp" +#include "Host/SystemInfo.hpp" +#include + +#include "Scene/Text/FontAtlasFactory.hpp" +#include "Host/ResourceLoader.hpp" + +using namespace OpenVulkano; + +TEST_CASE("Search system fonts") +{ + Logger::SetupLogger("", "tests.log"); + + // assume these fonts are present since they are default + std::filesystem::path path = SystemInfo::GetSystemFontPath("Arial"); + REQUIRE(path.filename() == "arial.ttf"); + + // assume these fonts are present since they are default + path = SystemInfo::GetSystemFontPath("Times New Roman"); + REQUIRE(path.filename() == "times.ttf"); + + path = SystemInfo::GetSystemFontPath("Arial Bold Italic"); + REQUIRE(path.filename() == "arialbi.ttf"); + + path = SystemInfo::GetSystemFontPath("NON-EXISTING Font"); + REQUIRE(path.empty()); +} From dbc52eff4293986504babd47d3cbb2e51080e608 Mon Sep 17 00:00:00 2001 From: ohyzha Date: Fri, 17 Jan 2025 14:01:24 +0200 Subject: [PATCH 02/15] implement system fonts search for Linux --- openVulkanoCpp/Host/Linux/SystemInfo.cpp | 31 +++++++++++++++++++ tests/CMakeLists.txt | 11 +++++++ .../Linux/SystemFontsSearchTestsUbuntu.cpp | 28 +++++++++++++++++ tests/Host/Windows/SystemFontsSearchTests.cpp | 1 - 4 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 tests/Host/Linux/SystemFontsSearchTestsUbuntu.cpp diff --git a/openVulkanoCpp/Host/Linux/SystemInfo.cpp b/openVulkanoCpp/Host/Linux/SystemInfo.cpp index 896a9c8..a63d3f7 100644 --- a/openVulkanoCpp/Host/Linux/SystemInfo.cpp +++ b/openVulkanoCpp/Host/Linux/SystemInfo.cpp @@ -16,6 +16,9 @@ #include #include #include +#include +#include +#include namespace OpenVulkano { @@ -397,6 +400,34 @@ namespace OpenVulkano std::string SystemInfo::GetSystemFontPath(const std::string& fontName) { + // fontName -> fontPath + static std::map fontFilesMapping; + if (fontFilesMapping.empty()) + { + const std::filesystem::path fontsDir = "/usr/share/fonts/truetype"; + std::function GetFontFiles; + GetFontFiles = [&](const std::filesystem::path& dir) + { + for (const auto& file : std::filesystem::directory_iterator(dir)) + { + if (file.is_directory()) + { + GetFontFiles(dir / file); + } + else if (file.path().extension() == ".ttf") + { + // store font name without extension + fontFilesMapping[file.path().stem()] = file.path(); + } + } + }; + GetFontFiles(fontsDir); + } + + if (fontFilesMapping.contains(fontName)) + { + return fontFilesMapping.at(fontName); + } return ""; } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 85c0c4f..29345f3 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -15,6 +15,17 @@ if (APPLE) list(FILTER SOURCES EXCLUDE REGEX "ExeAppendedZipResourceLoader") endif() +if (LINUX) + find_program(LSB_RELEASE_EXEC lsb_release) + execute_process(COMMAND ${LSB_RELEASE_EXEC} -is + OUTPUT_VARIABLE LSB_RELEASE_ID_SHORT + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + if (NOT ${LSB_RELEASE_ID_SHORT} STREQUAL "Ubuntu") + list(FILTER SOURCES EXCLUDE REGEX "*Ubuntu*") + endif() +endif() + source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" FILES ${SOURCES}) file(GLOB_RECURSE RESOURCES "${ROOT_FOLDER}/resources/*.rc" "${ROOT_FOLDER}/resources/*.h") list(APPEND SOURCES ${RESOURCES}) diff --git a/tests/Host/Linux/SystemFontsSearchTestsUbuntu.cpp b/tests/Host/Linux/SystemFontsSearchTestsUbuntu.cpp new file mode 100644 index 0000000..6c2e372 --- /dev/null +++ b/tests/Host/Linux/SystemFontsSearchTestsUbuntu.cpp @@ -0,0 +1,28 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include +#include "Base/Logger.hpp" +#include "Host/SystemInfo.hpp" +#include "Scene/Text/FontAtlasFactory.hpp" +#include + +using namespace OpenVulkano; + +TEST_CASE("Search system fonts") +{ + Logger::SetupLogger("", "tests.log"); + + // assume these fonts are present since they are default + std::filesystem::path path = SystemInfo::GetSystemFontPath("Ubuntu-R"); + REQUIRE(path == "/usr/share/fonts/truetype/ubuntu/Ubuntu-R.ttf"); + + path = SystemInfo::GetSystemFontPath("UbuntuMono-BI"); + REQUIRE(!path.empty()); + + path = SystemInfo::GetSystemFontPath("NON-EXISTING Font"); + REQUIRE(path.empty()); +} diff --git a/tests/Host/Windows/SystemFontsSearchTests.cpp b/tests/Host/Windows/SystemFontsSearchTests.cpp index 4fdf919..c299d48 100644 --- a/tests/Host/Windows/SystemFontsSearchTests.cpp +++ b/tests/Host/Windows/SystemFontsSearchTests.cpp @@ -10,7 +10,6 @@ #include #include "Scene/Text/FontAtlasFactory.hpp" -#include "Host/ResourceLoader.hpp" using namespace OpenVulkano; From a5a1fd99d524aec327f9e77e000b07cc3f71a93e Mon Sep 17 00:00:00 2001 From: ohyzha Date: Sat, 18 Jan 2025 13:26:37 +0200 Subject: [PATCH 03/15] different improvements --- openVulkanoCpp/Base/Utils.hpp | 4 +- openVulkanoCpp/Host/SystemFontResolver.hpp | 22 +++++ openVulkanoCpp/Host/SystemInfo.hpp | 1 - .../Host/Windows/SystemFontResolver.cpp | 66 ++++++++++++++ openVulkanoCpp/Host/Windows/SystemInfo.cpp | 57 ------------ .../Scene/BitmapFontAtlasGenerator.cpp | 5 +- .../Scene/Text/FontAtlasFactory.cpp | 91 ++++++++++++------- .../Scene/Text/FontAtlasFactory.hpp | 49 +++------- tests/Host/Windows/SystemFontsSearchTests.cpp | 10 +- 9 files changed, 168 insertions(+), 137 deletions(-) create mode 100644 openVulkanoCpp/Host/SystemFontResolver.hpp create mode 100644 openVulkanoCpp/Host/Windows/SystemFontResolver.cpp diff --git a/openVulkanoCpp/Base/Utils.hpp b/openVulkanoCpp/Base/Utils.hpp index 7654bc9..1e56708 100644 --- a/openVulkanoCpp/Base/Utils.hpp +++ b/openVulkanoCpp/Base/Utils.hpp @@ -187,11 +187,11 @@ namespace OpenVulkano } template - static Array ReadFile(const T& filePath, bool emptyOnMissing = false, + static Array ReadFile(const T& filePath, bool emptyOnMissing = true, bool nullTerminateString = false); template - static Array ReadFile(const char (&filePath)[N], bool emptyOnMissing = false, + static Array ReadFile(const char (&filePath)[N], bool emptyOnMissing = true, bool nullTerminateString = false) { return ReadFile(std::string(filePath), emptyOnMissing, nullTerminateString); diff --git a/openVulkanoCpp/Host/SystemFontResolver.hpp b/openVulkanoCpp/Host/SystemFontResolver.hpp new file mode 100644 index 0000000..eb4c8b3 --- /dev/null +++ b/openVulkanoCpp/Host/SystemFontResolver.hpp @@ -0,0 +1,22 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include +#include + +namespace OpenVulkano +{ + class SystemFontResolver + { + public: + static std::string GetSystemFontPath(const std::string& fontName); + + private: + static std::map ReadSystemFonts(); + }; +} diff --git a/openVulkanoCpp/Host/SystemInfo.hpp b/openVulkanoCpp/Host/SystemInfo.hpp index f3a0b3e..7ab324c 100644 --- a/openVulkanoCpp/Host/SystemInfo.hpp +++ b/openVulkanoCpp/Host/SystemInfo.hpp @@ -69,7 +69,6 @@ namespace OpenVulkano static DeviceOrientation GetDeviceOrientation(); static void EnableDeviceOrientationEvents(); static InterfaceOrientation GetInterfaceOrientation(); - static std::string GetSystemFontPath(const std::string& fontName); static Event<> OnLowPowerModeChanged; static Event<> OnBatteryStateChanged; diff --git a/openVulkanoCpp/Host/Windows/SystemFontResolver.cpp b/openVulkanoCpp/Host/Windows/SystemFontResolver.cpp new file mode 100644 index 0000000..2d59940 --- /dev/null +++ b/openVulkanoCpp/Host/Windows/SystemFontResolver.cpp @@ -0,0 +1,66 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include "Host/SystemFontResolver.hpp" +#include +#include + +#define QFR_DESCRIPTION 1 + +namespace OpenVulkano +{ + std::string SystemFontResolver::GetSystemFontPath(const std::string& fontName) + { + // font name -> filename + static std::map fontFileMapping = ReadSystemFonts(); + // maybe check everything in lower case ? so that we can use Arial/arial as input parameter + auto it = fontFileMapping.find(fontName); + return it == fontFileMapping.end() ? "" : it->second; + } + + std::map SystemFontResolver::ReadSystemFonts() + { + std::map fontFileMapping; + // thank you Microsoft for function that is not even documented, but exists and it's the only function that + // can return real font name from font filename (e.g. font filename is times.ttf that corresponds to Times New Roman) + int(WINAPI* GetFontResourceInfoW)(wchar_t*, unsigned long*, void*, unsigned long); + *(FARPROC*) &GetFontResourceInfoW = GetProcAddress(GetModuleHandleA("gdi32"), "GetFontResourceInfoW"); + + std::wstring winDir; + winDir.resize(MAX_PATH); + UINT len = GetWindowsDirectoryW(winDir.data(), MAX_PATH); + winDir.resize(len); + std::filesystem::path fontsDir = std::filesystem::path(winDir) / "Fonts"; + for (const auto& fontFilename : std::filesystem::directory_iterator(fontsDir)) + { + unsigned long size = 0; + std::wstring ws = fontFilename.path().wstring(); + if (!GetFontResourceInfoW(ws.data(), &size, NULL, QFR_DESCRIPTION)) + { + continue; + } + std::wstring fontName; + fontName.resize(size); + if (GetFontResourceInfoW(ws.data(), &size, fontName.data(), QFR_DESCRIPTION)) + { + // remove null-terminated characters since size is always bigger than needed + std::string fontNameCropped(fontName.begin(), fontName.end()); + size_t realSize = 0; + for (; realSize < fontNameCropped.size(); realSize++) + { + if (fontNameCropped[realSize] == '\0') + { + break; + } + } + fontNameCropped.resize(realSize); + fontFileMapping[std::move(fontNameCropped)] = fontFilename.path().string(); + } + } + return fontFileMapping; + } + +} diff --git a/openVulkanoCpp/Host/Windows/SystemInfo.cpp b/openVulkanoCpp/Host/Windows/SystemInfo.cpp index 3366009..bea3c6b 100644 --- a/openVulkanoCpp/Host/Windows/SystemInfo.cpp +++ b/openVulkanoCpp/Host/Windows/SystemInfo.cpp @@ -19,9 +19,6 @@ #include #include #include -#include -#include -#include // NOTE(vb): Windows defines macros like GetUserName that are used to automatically select the appropriate function version (GetUserNameA for ANSI and GetUserNameW for Unicode) // based on whether the _UNICODE macro is defined, so we manually undefine these macros to avoid naming collisions. @@ -31,8 +28,6 @@ #pragma comment(lib, "PowrProf.lib") #pragma comment(lib, "wbemuuid.lib") -#define QFR_DESCRIPTION 1 - namespace OpenVulkano { namespace @@ -590,56 +585,4 @@ namespace OpenVulkano return InterfaceOrientation::Landscape; } } - - std::string SystemInfo::GetSystemFontPath(const std::string& fontName) - { - // font name -> filename - static std::map fontFileMapping; - if (fontFileMapping.empty()) - { - // thank you Microsoft for function that is not even documented, but exists and it's the only function that - // can return real font name from font filename (e.g. font filename is times.ttf that corresponds to Times New Roman) - int(WINAPI * GetFontResourceInfoW)(wchar_t*, unsigned long*, void*, unsigned long); - *(FARPROC*) &GetFontResourceInfoW = GetProcAddress(GetModuleHandleA("gdi32"), "GetFontResourceInfoW"); - - std::wstring winDir; - winDir.resize(MAX_PATH); - UINT len = GetWindowsDirectoryW(winDir.data(), MAX_PATH); - winDir.resize(len); - std::filesystem::path fontsDir = std::filesystem::path(winDir) / "Fonts"; - for (const auto& fontFilename : std::filesystem::directory_iterator(fontsDir)) - { - unsigned long size = 0; - std::wstring ws = fontFilename.path().wstring(); - if (!GetFontResourceInfoW(ws.data(), &size, NULL, QFR_DESCRIPTION)) - { - continue; - } - std::wstring fontName; - fontName.resize(size); - if (GetFontResourceInfoW(ws.data(), &size, fontName.data(), QFR_DESCRIPTION)) - { - // remove null-terminated characters since size is always bigger than needed - std::string fontNameCropped(fontName.begin(), fontName.end()); - size_t realSize = 0; - for (; realSize < fontNameCropped.size(); realSize++) - { - if (fontNameCropped[realSize] == '\0') - { - break; - } - } - fontNameCropped.resize(realSize); - fontFileMapping[std::move(fontNameCropped)] = fontFilename.path().string(); - } - } - } - - // maybe check everything in lower case ? so that we can use Arial/arial as input parameter - if (fontFileMapping.contains(fontName)) - { - return fontFileMapping.at(fontName); - } - return ""; - } } \ No newline at end of file diff --git a/openVulkanoCpp/Scene/BitmapFontAtlasGenerator.cpp b/openVulkanoCpp/Scene/BitmapFontAtlasGenerator.cpp index d94d884..634600b 100644 --- a/openVulkanoCpp/Scene/BitmapFontAtlasGenerator.cpp +++ b/openVulkanoCpp/Scene/BitmapFontAtlasGenerator.cpp @@ -157,9 +157,8 @@ namespace OpenVulkano::Scene + glyph.firstGlyphByteInAtlas; for (int row = 0; row < slot->bitmap.rows; row++) { - std::memcpy(baseAddress + row * m_atlasData->GetTexture()->resolution.x, - &slot->bitmap.buffer[(slot->bitmap.rows - 1 - row) * slot->bitmap.pitch], - slot->bitmap.width); + std::memcpy(baseAddress - row * m_atlasData->GetTexture()->resolution.x, + &slot->bitmap.buffer[row * slot->bitmap.pitch], slot->bitmap.width); } } else diff --git a/openVulkanoCpp/Scene/Text/FontAtlasFactory.cpp b/openVulkanoCpp/Scene/Text/FontAtlasFactory.cpp index fd0427d..a6f4ba2 100644 --- a/openVulkanoCpp/Scene/Text/FontAtlasFactory.cpp +++ b/openVulkanoCpp/Scene/Text/FontAtlasFactory.cpp @@ -7,73 +7,100 @@ #include "FontAtlasFactory.hpp" #include "Scene/SdfFontAtlasGenerator.hpp" #include "Scene/BitmapFontAtlasGenerator.hpp" -#include "Host/SystemInfo.hpp" +#include "Host/SystemFontResolver.hpp" +#include "Base/Logger.hpp" +#include "Host/ResourceLoader.hpp" +#include namespace OpenVulkano::Scene { - FontAtlas::Ptr FontAtlasFactory::GetFontAtlasScalable(const std::string& fontIdentifier, - const std::set& charset, bool msdf) const + FontAtlasFactory::FontIdentifier::FontIdentifier(const std::string& font_, const std::set& charset, + SubpixelLayout subpixelLayout_, float ptSize_, + FontAtlasType atlasType_) + : font(font_), subpixelLayout(subpixelLayout_), ptSize(ptSize_), atlasType(atlasType_) { - const std::string fileName = FindFont(fontIdentifier); - if (fileName.empty()) + std::for_each(charset.begin(), charset.end(), [&](uint32_t c) { charsetHash ^= c; }); + } + + bool FontAtlasFactory::FontIdentifier::FontIdentifier::operator<(const FontIdentifier& other) const + { + return std::tie(atlasType, charsetHash, ptSize, subpixelLayout, font) + < std::tie(other.atlasType, other.charsetHash, other.ptSize, other.subpixelLayout, other.font); + } + + FontAtlas::Ptr FontAtlasFactory::GetFontAtlasScalable(const std::string& fontIdentifier, bool msdf, + const std::set& charset) const + { + const auto& fontData = FindFont(fontIdentifier); + if (fontData.Empty()) { + Logger::DATA->warn("Could not find font {}", fontIdentifier); return nullptr; } - const std::set& setRef = (charset.empty() ? FontAtlasGeneratorBase::LoadAllGlyphs(fontIdentifier) : charset); - FontIdentifier id(fontIdentifier, setRef, SubpixelLayout::UNKNOWN, 0, msdf); - if (m_atlasesCache.contains(id)) + const std::set& setRef = (charset.empty() ? FontAtlasGeneratorBase::LoadAllGlyphs(fontData) : charset); + FontIdentifier id(fontIdentifier, setRef, SubpixelLayout::UNKNOWN, 0, + msdf ? FontAtlasType::MSDF : FontAtlasType::SDF); + + auto it = m_atlasesCache.find(id); + if (it != m_atlasesCache.end()) { - return m_atlasesCache.at(id); + return it->second; } if (msdf) { MsdfFontAtlasGenerator msdfGen; - msdfGen.GenerateAtlas(fileName, setRef); - m_atlasesCache[id] = msdfGen.GetAtlas(); - return m_atlasesCache.at(id); + msdfGen.GenerateAtlas(fontData, setRef); + return m_atlasesCache.insert({ id, msdfGen.GetAtlas() }).first->second; } SdfFontAtlasGenerator sdfGen; - sdfGen.GenerateAtlas(fileName, setRef); - m_atlasesCache[id] = sdfGen.GetAtlas(); - return m_atlasesCache.at(id); + sdfGen.GenerateAtlas(fontData, setRef); + return m_atlasesCache.insert({ id, sdfGen.GetAtlas() }).first->second; } FontAtlas::Ptr FontAtlasFactory::GetFontAtlas(const std::string& fontIdentifier, float ptSize, - const std::set& charset, - std::optional subpixelLayout) const + std::optional subpixelLayout, + const std::set& charset) const { - const std::string fileName = FindFont(fontIdentifier); - if (fileName.empty()) + const auto& fontData = FindFont(fontIdentifier); + if (fontData.Empty()) { + Logger::DATA->warn("Could not find font {}", fontIdentifier); return nullptr; } - const std::set& setRef = (charset.empty() ? FontAtlasGeneratorBase::LoadAllGlyphs(fontIdentifier) : charset); - FontIdentifier id(fontIdentifier, setRef, subpixelLayout.value_or(SubpixelLayout::UNKNOWN), ptSize, false); - if (m_atlasesCache.contains(id)) + const std::set& setRef = (charset.empty() ? FontAtlasGeneratorBase::LoadAllGlyphs(fontData) : charset); + FontIdentifier id(fontIdentifier, setRef, subpixelLayout.value_or(SubpixelLayout::UNKNOWN), ptSize, + subpixelLayout ? FontAtlasType::BITMAP_SUBPIXEL : FontAtlasType::BITMAP); + + auto it = m_atlasesCache.find(id); + if (it != m_atlasesCache.end()) { - return m_atlasesCache.at(id); + return it->second; } FontPixelSizeConfig cfg(ptSize); BitmapFontAtlasGenerator bitmapGen(cfg, subpixelLayout); - bitmapGen.GenerateAtlas(fileName, setRef); - m_atlasesCache[id] = bitmapGen.GetAtlas(); - return m_atlasesCache.at(id); + bitmapGen.GenerateAtlas(fontData, setRef); + return m_atlasesCache.insert({ id, bitmapGen.GetAtlas() }).first->second; } - std::string FontAtlasFactory::FindFont(const std::string& fontIdentifier) const + Array FontAtlasFactory::FindFont(const std::string& fontIdentifier) const { - if (!std::filesystem::exists(fontIdentifier)) + Array resource = ResourceLoader::GetInstance().GetResource(fontIdentifier); + if (resource.Empty()) { - if (!m_allowSystemFonts) + if (!std::filesystem::exists(fontIdentifier)) { - return ""; + if (!m_allowSystemFonts) + { + return {}; + } + return Utils::ReadFile(SystemFontResolver::GetSystemFontPath(fontIdentifier)); } - return SystemInfo::GetSystemFontPath(fontIdentifier); + return Utils::ReadFile(fontIdentifier); } - return fontIdentifier; + return resource; } } diff --git a/openVulkanoCpp/Scene/Text/FontAtlasFactory.hpp b/openVulkanoCpp/Scene/Text/FontAtlasFactory.hpp index 31ed708..22a8e53 100644 --- a/openVulkanoCpp/Scene/Text/FontAtlasFactory.hpp +++ b/openVulkanoCpp/Scene/Text/FontAtlasFactory.hpp @@ -8,6 +8,7 @@ #include "FontAtlas.hpp" #include "Scene/SubpixelLayout.hpp" +#include "Data/Containers/Array.hpp" namespace OpenVulkano::Scene { @@ -16,54 +17,28 @@ namespace OpenVulkano::Scene struct FontIdentifier { FontIdentifier(const std::string& font_, const std::set& charset, SubpixelLayout subpixelLayout_, - float ptSize_, bool msdf_) - : font(font_), subpixelLayout(subpixelLayout_), ptSize(ptSize_), msdf(msdf_) - { - std::for_each(charset.begin(), charset.end(), [&](uint32_t c) { charsetHash ^= c; }); - } - + float ptSize_, FontAtlasType atlasType_); + bool operator<(const FontIdentifier& other) const; + std::string font; - size_t charsetHash = 0; + uint32_t charsetHash = 0; SubpixelLayout subpixelLayout = SubpixelLayout::UNKNOWN; float ptSize = 0; - bool msdf = true; - - bool operator<(const FontIdentifier& other) const - { - if (font != other.font) - { - return font < other.font; - } - if (charsetHash != other.charsetHash) - { - return charsetHash < other.charsetHash; - } - if (subpixelLayout != other.subpixelLayout) - { - return subpixelLayout < other.subpixelLayout; - } - if (ptSize != other.ptSize) - { - return ptSize < other.ptSize; - } - return msdf < other.msdf; - } - + FontAtlasType atlasType; }; public: FontAtlasFactory(bool allowSystemFonts = true) : m_allowSystemFonts(allowSystemFonts) {} - [[nodiscard]] FontAtlas::Ptr GetFontAtlasScalable(const std::string& fontIdentifier, - const std::set& charset = {}, - bool msdf = true) const; + [[nodiscard]] FontAtlas::Ptr GetFontAtlasScalable(const std::string& fontIdentifier, bool msdf = true, + const std::set& charset = {}) const; [[nodiscard]] FontAtlas::Ptr GetFontAtlas(const std::string& fontIdentifier, float ptSize, - const std::set& charset = {}, - std::optional subpixelLayout = std::nullopt) const; + std::optional subpixelLayout = std::nullopt, + const std::set& charset = {}) const; private: - std::string FindFont(const std::string& fontFile) const; + Array FindFont(const std::string& fontFile) const; private: - bool m_allowSystemFonts; + const bool m_allowSystemFonts; mutable std::map m_atlasesCache; }; } diff --git a/tests/Host/Windows/SystemFontsSearchTests.cpp b/tests/Host/Windows/SystemFontsSearchTests.cpp index c299d48..e478e57 100644 --- a/tests/Host/Windows/SystemFontsSearchTests.cpp +++ b/tests/Host/Windows/SystemFontsSearchTests.cpp @@ -6,7 +6,7 @@ #include #include "Base/Logger.hpp" -#include "Host/SystemInfo.hpp" +#include "Host/SystemFontResolver.hpp" #include #include "Scene/Text/FontAtlasFactory.hpp" @@ -18,16 +18,16 @@ TEST_CASE("Search system fonts") Logger::SetupLogger("", "tests.log"); // assume these fonts are present since they are default - std::filesystem::path path = SystemInfo::GetSystemFontPath("Arial"); + std::filesystem::path path = SystemFontResolver::GetSystemFontPath("Arial"); REQUIRE(path.filename() == "arial.ttf"); // assume these fonts are present since they are default - path = SystemInfo::GetSystemFontPath("Times New Roman"); + path = SystemFontResolver::GetSystemFontPath("Times New Roman"); REQUIRE(path.filename() == "times.ttf"); - path = SystemInfo::GetSystemFontPath("Arial Bold Italic"); + path = SystemFontResolver::GetSystemFontPath("Arial Bold Italic"); REQUIRE(path.filename() == "arialbi.ttf"); - path = SystemInfo::GetSystemFontPath("NON-EXISTING Font"); + path = SystemFontResolver::GetSystemFontPath("NON-EXISTING Font"); REQUIRE(path.empty()); } From 5a5bac8479238ee016e2db2d049161a78eed1855 Mon Sep 17 00:00:00 2001 From: ohyzha Date: Sat, 18 Jan 2025 16:14:02 +0200 Subject: [PATCH 04/15] revert old changes --- .../Host/Linux/SystemFontResolver.cpp | 21 +++++++++++ openVulkanoCpp/Host/Linux/SystemInfo.cpp | 37 ------------------- .../Host/MacOS/SystemFontResolver.cpp | 21 +++++++++++ openVulkanoCpp/Host/MacOS/SystemInfo.mm | 5 --- .../Host/iOS/SystemFontResolver.cpp | 21 +++++++++++ openVulkanoCpp/Host/iOS/SystemInfo.mm | 5 --- 6 files changed, 63 insertions(+), 47 deletions(-) create mode 100644 openVulkanoCpp/Host/Linux/SystemFontResolver.cpp create mode 100644 openVulkanoCpp/Host/MacOS/SystemFontResolver.cpp create mode 100644 openVulkanoCpp/Host/iOS/SystemFontResolver.cpp diff --git a/openVulkanoCpp/Host/Linux/SystemFontResolver.cpp b/openVulkanoCpp/Host/Linux/SystemFontResolver.cpp new file mode 100644 index 0000000..f370d2c --- /dev/null +++ b/openVulkanoCpp/Host/Linux/SystemFontResolver.cpp @@ -0,0 +1,21 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include "Host/SystemFontResolver.hpp" +#include + +namespace OpenVulkano +{ + std::string SystemFontResolver::GetSystemFontPath(const std::string& fontName) + { + return ""; + } + + std::map SystemFontResolver::ReadSystemFonts() + { + return {}; + } +} diff --git a/openVulkanoCpp/Host/Linux/SystemInfo.cpp b/openVulkanoCpp/Host/Linux/SystemInfo.cpp index a63d3f7..19fe0cf 100644 --- a/openVulkanoCpp/Host/Linux/SystemInfo.cpp +++ b/openVulkanoCpp/Host/Linux/SystemInfo.cpp @@ -16,9 +16,6 @@ #include #include #include -#include -#include -#include namespace OpenVulkano { @@ -397,38 +394,4 @@ namespace OpenVulkano { return InterfaceOrientation::Landscape; // TODO? } - - std::string SystemInfo::GetSystemFontPath(const std::string& fontName) - { - // fontName -> fontPath - static std::map fontFilesMapping; - if (fontFilesMapping.empty()) - { - const std::filesystem::path fontsDir = "/usr/share/fonts/truetype"; - std::function GetFontFiles; - GetFontFiles = [&](const std::filesystem::path& dir) - { - for (const auto& file : std::filesystem::directory_iterator(dir)) - { - if (file.is_directory()) - { - GetFontFiles(dir / file); - } - else if (file.path().extension() == ".ttf") - { - // store font name without extension - fontFilesMapping[file.path().stem()] = file.path(); - } - } - }; - GetFontFiles(fontsDir); - } - - if (fontFilesMapping.contains(fontName)) - { - return fontFilesMapping.at(fontName); - } - return ""; - } - } \ No newline at end of file diff --git a/openVulkanoCpp/Host/MacOS/SystemFontResolver.cpp b/openVulkanoCpp/Host/MacOS/SystemFontResolver.cpp new file mode 100644 index 0000000..f370d2c --- /dev/null +++ b/openVulkanoCpp/Host/MacOS/SystemFontResolver.cpp @@ -0,0 +1,21 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include "Host/SystemFontResolver.hpp" +#include + +namespace OpenVulkano +{ + std::string SystemFontResolver::GetSystemFontPath(const std::string& fontName) + { + return ""; + } + + std::map SystemFontResolver::ReadSystemFonts() + { + return {}; + } +} diff --git a/openVulkanoCpp/Host/MacOS/SystemInfo.mm b/openVulkanoCpp/Host/MacOS/SystemInfo.mm index db95584..d404d08 100644 --- a/openVulkanoCpp/Host/MacOS/SystemInfo.mm +++ b/openVulkanoCpp/Host/MacOS/SystemInfo.mm @@ -202,9 +202,4 @@ namespace OpenVulkano { return InterfaceOrientation::Landscape; //TODO? } - - std::string SystemInfo::GetSystemFontPath(const std::string& fontName) - { - return ""; - } } diff --git a/openVulkanoCpp/Host/iOS/SystemFontResolver.cpp b/openVulkanoCpp/Host/iOS/SystemFontResolver.cpp new file mode 100644 index 0000000..f370d2c --- /dev/null +++ b/openVulkanoCpp/Host/iOS/SystemFontResolver.cpp @@ -0,0 +1,21 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include "Host/SystemFontResolver.hpp" +#include + +namespace OpenVulkano +{ + std::string SystemFontResolver::GetSystemFontPath(const std::string& fontName) + { + return ""; + } + + std::map SystemFontResolver::ReadSystemFonts() + { + return {}; + } +} diff --git a/openVulkanoCpp/Host/iOS/SystemInfo.mm b/openVulkanoCpp/Host/iOS/SystemInfo.mm index 17b7855..f19455f 100644 --- a/openVulkanoCpp/Host/iOS/SystemInfo.mm +++ b/openVulkanoCpp/Host/iOS/SystemInfo.mm @@ -288,9 +288,4 @@ namespace OpenVulkano } return InterfaceOrientation::Landscape; } - - std::string SystemInfo::GetSystemFontPath(const std::string& fontName) - { - return ""; - } } From 7670100d8122e257e920e5b8141a4fb2b6d137fc Mon Sep 17 00:00:00 2001 From: ohyzha Date: Sat, 18 Jan 2025 16:56:00 +0200 Subject: [PATCH 05/15] reimplement system fonts retrieval for Linux using fontconfig --- .vscode/settings.json | 96 +++++++++++++++++++ .../Host/Linux/SystemFontResolver.cpp | 43 ++++++++- .../Linux/SystemFontsSearchTestsUbuntu.cpp | 11 +-- 3 files changed, 142 insertions(+), 8 deletions(-) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..77a0b44 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,96 @@ +{ + "files.associations": { + "cctype": "cpp", + "clocale": "cpp", + "cmath": "cpp", + "csetjmp": "cpp", + "csignal": "cpp", + "cstdarg": "cpp", + "cstddef": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "cstring": "cpp", + "ctime": "cpp", + "cwchar": "cpp", + "cwctype": "cpp", + "*.ipp": "cpp", + "condition_variable": "cpp", + "thread": "cpp", + "*.inc": "cpp", + "any": "cpp", + "array": "cpp", + "atomic": "cpp", + "hash_map": "cpp", + "hash_set": "cpp", + "strstream": "cpp", + "barrier": "cpp", + "bit": "cpp", + "*.tcc": "cpp", + "bitset": "cpp", + "cfenv": "cpp", + "charconv": "cpp", + "chrono": "cpp", + "cinttypes": "cpp", + "codecvt": "cpp", + "compare": "cpp", + "complex": "cpp", + "concepts": "cpp", + "coroutine": "cpp", + "cstdint": "cpp", + "deque": "cpp", + "forward_list": "cpp", + "list": "cpp", + "map": "cpp", + "set": "cpp", + "string": "cpp", + "unordered_map": "cpp", + "unordered_set": "cpp", + "vector": "cpp", + "exception": "cpp", + "algorithm": "cpp", + "functional": "cpp", + "iterator": "cpp", + "memory": "cpp", + "memory_resource": "cpp", + "numeric": "cpp", + "optional": "cpp", + "random": "cpp", + "ratio": "cpp", + "regex": "cpp", + "source_location": "cpp", + "string_view": "cpp", + "system_error": "cpp", + "tuple": "cpp", + "type_traits": "cpp", + "utility": "cpp", + "rope": "cpp", + "slist": "cpp", + "fstream": "cpp", + "future": "cpp", + "initializer_list": "cpp", + "iomanip": "cpp", + "iosfwd": "cpp", + "iostream": "cpp", + "istream": "cpp", + "latch": "cpp", + "limits": "cpp", + "mutex": "cpp", + "new": "cpp", + "numbers": "cpp", + "ostream": "cpp", + "ranges": "cpp", + "scoped_allocator": "cpp", + "semaphore": "cpp", + "shared_mutex": "cpp", + "span": "cpp", + "sstream": "cpp", + "stdexcept": "cpp", + "stop_token": "cpp", + "streambuf": "cpp", + "syncstream": "cpp", + "typeindex": "cpp", + "typeinfo": "cpp", + "valarray": "cpp", + "variant": "cpp" + } +} \ No newline at end of file diff --git a/openVulkanoCpp/Host/Linux/SystemFontResolver.cpp b/openVulkanoCpp/Host/Linux/SystemFontResolver.cpp index f370d2c..c715e4c 100644 --- a/openVulkanoCpp/Host/Linux/SystemFontResolver.cpp +++ b/openVulkanoCpp/Host/Linux/SystemFontResolver.cpp @@ -5,17 +5,56 @@ */ #include "Host/SystemFontResolver.hpp" -#include +#include "Base/Logger.hpp" +#include namespace OpenVulkano { std::string SystemFontResolver::GetSystemFontPath(const std::string& fontName) { + // fontName -> fontPath + static std::map fontFilesMapping = ReadSystemFonts(); + auto it = fontFilesMapping.find(fontName); + if (it != fontFilesMapping.end()) + { + return it->second; + } return ""; } std::map SystemFontResolver::ReadSystemFonts() { - return {}; + FcConfig* config = FcInitLoadConfigAndFonts(); + FcPattern* pat = FcPatternCreate(); + FcObjectSet* os = FcObjectSetBuild(FC_FAMILY, FC_STYLE, FC_LANG, FC_FILE, NULL); + FcFontSet* fs = FcFontList(config, pat, os); + if (!fs) + { + Logger::DATA->warn("Could not get system fonts"); + FcFontSetDestroy(fs); + FcObjectSetDestroy(os); + FcPatternDestroy(pat); + return {}; + } + + std::map fontFilesMapping; + for (int i = 0; i < fs->nfont; ++i) + { + FcPattern* font = fs->fonts[i]; + FcChar8* file, *style, *family; + if (FcPatternGetString(font, FC_FILE, 0, &file) == FcResultMatch && + FcPatternGetString(font, FC_FAMILY, 0, &family) == FcResultMatch && + FcPatternGetString(font, FC_STYLE, 0, &style) == FcResultMatch) + { + fontFilesMapping[std::string(reinterpret_cast(family)) + " " + + std::string(reinterpret_cast(style))] + = std::string(reinterpret_cast(file)); + } + } + FcFontSetDestroy(fs); + FcObjectSetDestroy(os); + FcPatternDestroy(pat); + FcConfigDestroy(config); + return fontFilesMapping; } } diff --git a/tests/Host/Linux/SystemFontsSearchTestsUbuntu.cpp b/tests/Host/Linux/SystemFontsSearchTestsUbuntu.cpp index 6c2e372..743631b 100644 --- a/tests/Host/Linux/SystemFontsSearchTestsUbuntu.cpp +++ b/tests/Host/Linux/SystemFontsSearchTestsUbuntu.cpp @@ -5,9 +5,8 @@ */ #include +#include "Host/SystemFontResolver.hpp" #include "Base/Logger.hpp" -#include "Host/SystemInfo.hpp" -#include "Scene/Text/FontAtlasFactory.hpp" #include using namespace OpenVulkano; @@ -17,12 +16,12 @@ TEST_CASE("Search system fonts") Logger::SetupLogger("", "tests.log"); // assume these fonts are present since they are default - std::filesystem::path path = SystemInfo::GetSystemFontPath("Ubuntu-R"); + std::string path = SystemFontResolver::GetSystemFontPath("Ubuntu Regular"); REQUIRE(path == "/usr/share/fonts/truetype/ubuntu/Ubuntu-R.ttf"); - path = SystemInfo::GetSystemFontPath("UbuntuMono-BI"); - REQUIRE(!path.empty()); + path = SystemFontResolver::GetSystemFontPath("Ubuntu Mono Bold Italic"); + REQUIRE(path == "/usr/share/fonts/truetype/ubuntu/UbuntuMono-BI.ttf"); - path = SystemInfo::GetSystemFontPath("NON-EXISTING Font"); + path = SystemFontResolver::GetSystemFontPath("NON-EXISTING Font"); REQUIRE(path.empty()); } From f1f377e5ca6ae9dab2b29a1454053c427b3dc624 Mon Sep 17 00:00:00 2001 From: ohyzha Date: Sat, 18 Jan 2025 17:11:25 +0200 Subject: [PATCH 06/15] add default ubuntu fonts to docker image --- .gitea/workflows/build_pc.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitea/workflows/build_pc.yml b/.gitea/workflows/build_pc.yml index 5101ed2..b9369bd 100644 --- a/.gitea/workflows/build_pc.yml +++ b/.gitea/workflows/build_pc.yml @@ -32,8 +32,8 @@ jobs: - name: Install Dev Packages if: matrix.os == 'ubuntu-latest' run: > - sudo apt update && sudo apt install -y extra-cmake-modules libwayland-dev libxkbcommon-dev xorg-dev libarchive-dev libassimp-dev ninja-build glslang-tools glslang-dev unzip zip libcurl4-openssl-dev libfreetype-dev libjpeg-turbo8-dev nasm - && sudo wget https://sourceforge.net/projects/bin2c/files/1.1/bin2c-1.1.zip && sudo unzip bin2c-1.1.zip && cd bin2c && sudo gcc -o bin2c bin2c.c && sudo mv bin2c /usr/bin + sudo apt update && sudo apt install -y extra-cmake-modules libwayland-dev fonts-ubuntu libxkbcommon-dev xorg-dev libarchive-dev libassimp-dev ninja-build glslang-tools glslang-dev unzip zip libcurl4-openssl-dev libfreetype-dev libjpeg-turbo8-dev nasm + && sudo wget https://sourceforge.net/projects/bin2c/files/1.1/bin2c-1.1.zip && sudo unzip bin2c-1.1.zip && cd bin2c && sudo gcc -o bin2c bin2c.c && sudo mv bin2c /usr/bin && fc-cache -f -v - name: Get cmake checks from cache uses: actions/cache@v4 From ee71d265d020ab2c95a4db78035672968a65e96d Mon Sep 17 00:00:00 2001 From: ohyzha Date: Mon, 20 Jan 2025 13:05:49 +0200 Subject: [PATCH 07/15] add ToLower helper function --- openVulkanoCpp/Base/Utils.hpp | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/openVulkanoCpp/Base/Utils.hpp b/openVulkanoCpp/Base/Utils.hpp index 1e56708..675d45c 100644 --- a/openVulkanoCpp/Base/Utils.hpp +++ b/openVulkanoCpp/Base/Utils.hpp @@ -157,6 +157,24 @@ namespace OpenVulkano return str.size() >= suffix.size() && 0 == str.compare(str.size()-suffix.size(), suffix.size(), suffix); } + static void ToLower(std::string& str) + { + std::transform(str.begin(), str.end(), str.begin(), [](char c) { std::tolower(c); }); + } + + static void ToLower(const std::string& str, char* dest) + { + std::transform(str.begin(), str.end(), dest, [](char c) { std::tolower(c); }); + } + + static std::string ToLower(const std::string& str) + { + std::string dest; + dest.reserve(str.size()); + std::transform(str.begin(), str.end(), dest.begin(), [](char c) { std::tolower(c); }); + return dest; + } + static std::pair SplitAtLastOccurrence(const std::string& str, char splitAt) { size_t pos = str.rfind(splitAt); @@ -187,11 +205,11 @@ namespace OpenVulkano } template - static Array ReadFile(const T& filePath, bool emptyOnMissing = true, + static Array ReadFile(const T& filePath, bool emptyOnMissing = false, bool nullTerminateString = false); template - static Array ReadFile(const char (&filePath)[N], bool emptyOnMissing = true, + static Array ReadFile(const char (&filePath)[N], bool emptyOnMissing = false, bool nullTerminateString = false) { return ReadFile(std::string(filePath), emptyOnMissing, nullTerminateString); From af86e63b3234f32c7c19dcd325182caaff8ef233 Mon Sep 17 00:00:00 2001 From: ohyzha Date: Mon, 20 Jan 2025 13:06:11 +0200 Subject: [PATCH 08/15] refactoring --- .../Host/MacOS/SystemFontResolver.cpp | 2 +- openVulkanoCpp/Host/SystemFontResolver.hpp | 2 +- .../Host/Windows/SystemFontResolver.cpp | 7 +++-- .../Scene/Text/FontAtlasFactory.cpp | 30 ++++++++++--------- .../Scene/Text/FontAtlasFactory.hpp | 2 +- 5 files changed, 23 insertions(+), 20 deletions(-) diff --git a/openVulkanoCpp/Host/MacOS/SystemFontResolver.cpp b/openVulkanoCpp/Host/MacOS/SystemFontResolver.cpp index f370d2c..b8d32c8 100644 --- a/openVulkanoCpp/Host/MacOS/SystemFontResolver.cpp +++ b/openVulkanoCpp/Host/MacOS/SystemFontResolver.cpp @@ -9,7 +9,7 @@ namespace OpenVulkano { - std::string SystemFontResolver::GetSystemFontPath(const std::string& fontName) + const std::string& SystemFontResolver::GetSystemFontPath(const std::string& fontName) { return ""; } diff --git a/openVulkanoCpp/Host/SystemFontResolver.hpp b/openVulkanoCpp/Host/SystemFontResolver.hpp index eb4c8b3..83dac87 100644 --- a/openVulkanoCpp/Host/SystemFontResolver.hpp +++ b/openVulkanoCpp/Host/SystemFontResolver.hpp @@ -14,7 +14,7 @@ namespace OpenVulkano class SystemFontResolver { public: - static std::string GetSystemFontPath(const std::string& fontName); + static const std::string& GetSystemFontPath(const std::string& fontName); private: static std::map ReadSystemFonts(); diff --git a/openVulkanoCpp/Host/Windows/SystemFontResolver.cpp b/openVulkanoCpp/Host/Windows/SystemFontResolver.cpp index 2d59940..a3e908f 100644 --- a/openVulkanoCpp/Host/Windows/SystemFontResolver.cpp +++ b/openVulkanoCpp/Host/Windows/SystemFontResolver.cpp @@ -5,6 +5,7 @@ */ #include "Host/SystemFontResolver.hpp" +#include "Base/Utils.hpp" #include #include @@ -12,12 +13,11 @@ namespace OpenVulkano { - std::string SystemFontResolver::GetSystemFontPath(const std::string& fontName) + const std::string& SystemFontResolver::GetSystemFontPath(const std::string& fontName) { // font name -> filename static std::map fontFileMapping = ReadSystemFonts(); - // maybe check everything in lower case ? so that we can use Arial/arial as input parameter - auto it = fontFileMapping.find(fontName); + auto it = fontFileMapping.find(Utils::ToLower(fontName)); return it == fontFileMapping.end() ? "" : it->second; } @@ -57,6 +57,7 @@ namespace OpenVulkano } } fontNameCropped.resize(realSize); + Utils::ToLower(fontNameCropped); fontFileMapping[std::move(fontNameCropped)] = fontFilename.path().string(); } } diff --git a/openVulkanoCpp/Scene/Text/FontAtlasFactory.cpp b/openVulkanoCpp/Scene/Text/FontAtlasFactory.cpp index a6f4ba2..5b60fbc 100644 --- a/openVulkanoCpp/Scene/Text/FontAtlasFactory.cpp +++ b/openVulkanoCpp/Scene/Text/FontAtlasFactory.cpp @@ -60,7 +60,7 @@ namespace OpenVulkano::Scene } FontAtlas::Ptr FontAtlasFactory::GetFontAtlas(const std::string& fontIdentifier, float ptSize, - std::optional subpixelLayout, + SubpixelLayout subpixelLayout, const std::set& charset) const { const auto& fontData = FindFont(fontIdentifier); @@ -71,7 +71,7 @@ namespace OpenVulkano::Scene } const std::set& setRef = (charset.empty() ? FontAtlasGeneratorBase::LoadAllGlyphs(fontData) : charset); - FontIdentifier id(fontIdentifier, setRef, subpixelLayout.value_or(SubpixelLayout::UNKNOWN), ptSize, + FontIdentifier id(fontIdentifier, setRef, subpixelLayout, ptSize, subpixelLayout ? FontAtlasType::BITMAP_SUBPIXEL : FontAtlasType::BITMAP); auto it = m_atlasesCache.find(id); @@ -88,19 +88,21 @@ namespace OpenVulkano::Scene Array FontAtlasFactory::FindFont(const std::string& fontIdentifier) const { - Array resource = ResourceLoader::GetInstance().GetResource(fontIdentifier); - if (resource.Empty()) + try { - if (!std::filesystem::exists(fontIdentifier)) - { - if (!m_allowSystemFonts) - { - return {}; - } - return Utils::ReadFile(SystemFontResolver::GetSystemFontPath(fontIdentifier)); - } - return Utils::ReadFile(fontIdentifier); + return ResourceLoader::GetInstance().GetResource(fontIdentifier); } - return resource; + catch (...) + { + } + if (!std::filesystem::exists(fontIdentifier)) + { + if (!m_allowSystemFonts) + { + return {}; + } + return Utils::ReadFile(SystemFontResolver::GetSystemFontPath(fontIdentifier), true); + } + return Utils::ReadFile(fontIdentifier); } } diff --git a/openVulkanoCpp/Scene/Text/FontAtlasFactory.hpp b/openVulkanoCpp/Scene/Text/FontAtlasFactory.hpp index 22a8e53..3759c2c 100644 --- a/openVulkanoCpp/Scene/Text/FontAtlasFactory.hpp +++ b/openVulkanoCpp/Scene/Text/FontAtlasFactory.hpp @@ -31,7 +31,7 @@ namespace OpenVulkano::Scene [[nodiscard]] FontAtlas::Ptr GetFontAtlasScalable(const std::string& fontIdentifier, bool msdf = true, const std::set& charset = {}) const; [[nodiscard]] FontAtlas::Ptr GetFontAtlas(const std::string& fontIdentifier, float ptSize, - std::optional subpixelLayout = std::nullopt, + SubpixelLayout subpixelLayout = SubpixelLayout::UNKNOWN, const std::set& charset = {}) const; private: From 2aa11eb113a79b5458b45c83bc41219341760c18 Mon Sep 17 00:00:00 2001 From: ohyzha Date: Mon, 20 Jan 2025 13:08:27 +0200 Subject: [PATCH 09/15] fix ToLower --- openVulkanoCpp/Base/Utils.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openVulkanoCpp/Base/Utils.hpp b/openVulkanoCpp/Base/Utils.hpp index 675d45c..f9a8a78 100644 --- a/openVulkanoCpp/Base/Utils.hpp +++ b/openVulkanoCpp/Base/Utils.hpp @@ -159,19 +159,19 @@ namespace OpenVulkano static void ToLower(std::string& str) { - std::transform(str.begin(), str.end(), str.begin(), [](char c) { std::tolower(c); }); + std::transform(str.begin(), str.end(), str.begin(), [](char c) { return std::tolower(c); }); } static void ToLower(const std::string& str, char* dest) { - std::transform(str.begin(), str.end(), dest, [](char c) { std::tolower(c); }); + std::transform(str.begin(), str.end(), dest, [](char c) { return std::tolower(c); }); } static std::string ToLower(const std::string& str) { std::string dest; dest.reserve(str.size()); - std::transform(str.begin(), str.end(), dest.begin(), [](char c) { std::tolower(c); }); + std::transform(str.begin(), str.end(), dest.begin(), [](char c) { return std::tolower(c); }); return dest; } From d912270c74a283b37580902292d55dbf0c53674e Mon Sep 17 00:00:00 2001 From: ohyzha Date: Mon, 20 Jan 2025 13:17:32 +0200 Subject: [PATCH 10/15] remove unnecessary try catch block --- .../Scene/Text/FontAtlasFactory.cpp | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/openVulkanoCpp/Scene/Text/FontAtlasFactory.cpp b/openVulkanoCpp/Scene/Text/FontAtlasFactory.cpp index 5b60fbc..d28c5d9 100644 --- a/openVulkanoCpp/Scene/Text/FontAtlasFactory.cpp +++ b/openVulkanoCpp/Scene/Text/FontAtlasFactory.cpp @@ -88,21 +88,19 @@ namespace OpenVulkano::Scene Array FontAtlasFactory::FindFont(const std::string& fontIdentifier) const { - try + Array resource = ResourceLoader::GetInstance().GetResource(fontIdentifier); + if (resource.Empty()) { - return ResourceLoader::GetInstance().GetResource(fontIdentifier); - } - catch (...) - { - } - if (!std::filesystem::exists(fontIdentifier)) - { - if (!m_allowSystemFonts) + if (!std::filesystem::exists(fontIdentifier)) { - return {}; + if (!m_allowSystemFonts) + { + return {}; + } + return Utils::ReadFile(SystemFontResolver::GetSystemFontPath(fontIdentifier), true); } - return Utils::ReadFile(SystemFontResolver::GetSystemFontPath(fontIdentifier), true); + return Utils::ReadFile(fontIdentifier); } - return Utils::ReadFile(fontIdentifier); + return resource; } } From 635860b0c2a05e45c9b59ead079ed1b12f8351e0 Mon Sep 17 00:00:00 2001 From: ohyzha Date: Mon, 20 Jan 2025 13:59:08 +0200 Subject: [PATCH 11/15] fix ToLower again --- openVulkanoCpp/Base/Utils.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openVulkanoCpp/Base/Utils.hpp b/openVulkanoCpp/Base/Utils.hpp index f9a8a78..98b1640 100644 --- a/openVulkanoCpp/Base/Utils.hpp +++ b/openVulkanoCpp/Base/Utils.hpp @@ -170,7 +170,7 @@ namespace OpenVulkano static std::string ToLower(const std::string& str) { std::string dest; - dest.reserve(str.size()); + dest.resize(str.size()); std::transform(str.begin(), str.end(), dest.begin(), [](char c) { return std::tolower(c); }); return dest; } From 497ec7d09b197768aa0152276e41660677580f07 Mon Sep 17 00:00:00 2001 From: ohyzha Date: Mon, 20 Jan 2025 13:59:46 +0200 Subject: [PATCH 12/15] Linux side code refactoring --- .../Host/Linux/SystemFontResolver.cpp | 40 +++++++++---------- .../Linux/SystemFontsSearchTestsUbuntu.cpp | 6 +++ 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/openVulkanoCpp/Host/Linux/SystemFontResolver.cpp b/openVulkanoCpp/Host/Linux/SystemFontResolver.cpp index c715e4c..e228a63 100644 --- a/openVulkanoCpp/Host/Linux/SystemFontResolver.cpp +++ b/openVulkanoCpp/Host/Linux/SystemFontResolver.cpp @@ -6,34 +6,31 @@ #include "Host/SystemFontResolver.hpp" #include "Base/Logger.hpp" +#include "Base/Utils.hpp" #include +#include namespace OpenVulkano { - std::string SystemFontResolver::GetSystemFontPath(const std::string& fontName) + const std::string& SystemFontResolver::GetSystemFontPath(const std::string& fontName) { // fontName -> fontPath static std::map fontFilesMapping = ReadSystemFonts(); - auto it = fontFilesMapping.find(fontName); - if (it != fontFilesMapping.end()) - { - return it->second; - } - return ""; + static std::string fallbackString; + auto it = fontFilesMapping.find(Utils::ToLower(fontName)); + return it == fontFilesMapping.end() ? fallbackString : it->second; } std::map SystemFontResolver::ReadSystemFonts() { - FcConfig* config = FcInitLoadConfigAndFonts(); - FcPattern* pat = FcPatternCreate(); - FcObjectSet* os = FcObjectSetBuild(FC_FAMILY, FC_STYLE, FC_LANG, FC_FILE, NULL); - FcFontSet* fs = FcFontList(config, pat, os); + std::unique_ptr config(FcInitLoadConfigAndFonts(), &FcConfigDestroy); + std::unique_ptr pat(FcPatternCreate(), &FcPatternDestroy); + std::unique_ptr os(FcObjectSetBuild(FC_FAMILY, FC_STYLE, FC_LANG, FC_FILE, NULL), &FcObjectSetDestroy); + std::unique_ptr fs(FcFontList(config.get(), pat.get(), os.get()), &FcFontSetDestroy); + if (!fs) { Logger::DATA->warn("Could not get system fonts"); - FcFontSetDestroy(fs); - FcObjectSetDestroy(os); - FcPatternDestroy(pat); return {}; } @@ -41,20 +38,19 @@ namespace OpenVulkano for (int i = 0; i < fs->nfont; ++i) { FcPattern* font = fs->fonts[i]; - FcChar8* file, *style, *family; + FcChar8* file; + FcChar8 *style; + FcChar8 *family; if (FcPatternGetString(font, FC_FILE, 0, &file) == FcResultMatch && FcPatternGetString(font, FC_FAMILY, 0, &family) == FcResultMatch && FcPatternGetString(font, FC_STYLE, 0, &style) == FcResultMatch) { - fontFilesMapping[std::string(reinterpret_cast(family)) + " " + - std::string(reinterpret_cast(style))] - = std::string(reinterpret_cast(file)); + std::string fontFull = std::string(reinterpret_cast(family)) + " " + + std::string(reinterpret_cast(style)); + Utils::ToLower(fontFull); + fontFilesMapping[std::move(fontFull)] = std::string(reinterpret_cast(file)); } } - FcFontSetDestroy(fs); - FcObjectSetDestroy(os); - FcPatternDestroy(pat); - FcConfigDestroy(config); return fontFilesMapping; } } diff --git a/tests/Host/Linux/SystemFontsSearchTestsUbuntu.cpp b/tests/Host/Linux/SystemFontsSearchTestsUbuntu.cpp index 743631b..7db0018 100644 --- a/tests/Host/Linux/SystemFontsSearchTestsUbuntu.cpp +++ b/tests/Host/Linux/SystemFontsSearchTestsUbuntu.cpp @@ -19,9 +19,15 @@ TEST_CASE("Search system fonts") std::string path = SystemFontResolver::GetSystemFontPath("Ubuntu Regular"); REQUIRE(path == "/usr/share/fonts/truetype/ubuntu/Ubuntu-R.ttf"); + path = SystemFontResolver::GetSystemFontPath("ubuntu regular"); + REQUIRE(path == "/usr/share/fonts/truetype/ubuntu/Ubuntu-R.ttf"); + path = SystemFontResolver::GetSystemFontPath("Ubuntu Mono Bold Italic"); REQUIRE(path == "/usr/share/fonts/truetype/ubuntu/UbuntuMono-BI.ttf"); + path = SystemFontResolver::GetSystemFontPath("ubuntu mono bold italic"); + REQUIRE(path == "/usr/share/fonts/truetype/ubuntu/UbuntuMono-BI.ttf"); + path = SystemFontResolver::GetSystemFontPath("NON-EXISTING Font"); REQUIRE(path.empty()); } From 22b0db96577935ed1d5e3cc2e6e81d57d4cf8d2c Mon Sep 17 00:00:00 2001 From: ohyzha Date: Mon, 20 Jan 2025 14:17:33 +0200 Subject: [PATCH 13/15] add fallback strings --- openVulkanoCpp/Host/MacOS/SystemFontResolver.cpp | 3 ++- openVulkanoCpp/Host/Windows/SystemFontResolver.cpp | 3 ++- openVulkanoCpp/Host/iOS/SystemFontResolver.cpp | 5 +++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/openVulkanoCpp/Host/MacOS/SystemFontResolver.cpp b/openVulkanoCpp/Host/MacOS/SystemFontResolver.cpp index b8d32c8..617c36e 100644 --- a/openVulkanoCpp/Host/MacOS/SystemFontResolver.cpp +++ b/openVulkanoCpp/Host/MacOS/SystemFontResolver.cpp @@ -11,7 +11,8 @@ namespace OpenVulkano { const std::string& SystemFontResolver::GetSystemFontPath(const std::string& fontName) { - return ""; + static std::string fallbackString; + return fallbackString; } std::map SystemFontResolver::ReadSystemFonts() diff --git a/openVulkanoCpp/Host/Windows/SystemFontResolver.cpp b/openVulkanoCpp/Host/Windows/SystemFontResolver.cpp index a3e908f..516d2fd 100644 --- a/openVulkanoCpp/Host/Windows/SystemFontResolver.cpp +++ b/openVulkanoCpp/Host/Windows/SystemFontResolver.cpp @@ -17,8 +17,9 @@ namespace OpenVulkano { // font name -> filename static std::map fontFileMapping = ReadSystemFonts(); + static std::string fallbackString; auto it = fontFileMapping.find(Utils::ToLower(fontName)); - return it == fontFileMapping.end() ? "" : it->second; + return it == fontFileMapping.end() ? fallbackString : it->second; } std::map SystemFontResolver::ReadSystemFonts() diff --git a/openVulkanoCpp/Host/iOS/SystemFontResolver.cpp b/openVulkanoCpp/Host/iOS/SystemFontResolver.cpp index f370d2c..3aa69e1 100644 --- a/openVulkanoCpp/Host/iOS/SystemFontResolver.cpp +++ b/openVulkanoCpp/Host/iOS/SystemFontResolver.cpp @@ -9,9 +9,10 @@ namespace OpenVulkano { - std::string SystemFontResolver::GetSystemFontPath(const std::string& fontName) + const std::string& SystemFontResolver::GetSystemFontPath(const std::string& fontName) { - return ""; + static std::string fallbackString; + return fallbackString; } std::map SystemFontResolver::ReadSystemFonts() From f55fb37730bacf32b952af5aa46cceccdae0d31e Mon Sep 17 00:00:00 2001 From: ohyzha Date: Mon, 20 Jan 2025 17:04:07 +0200 Subject: [PATCH 14/15] minor changes --- openVulkanoCpp/Host/Linux/SystemFontResolver.cpp | 4 ++-- openVulkanoCpp/Host/Windows/SystemFontResolver.cpp | 9 +-------- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/openVulkanoCpp/Host/Linux/SystemFontResolver.cpp b/openVulkanoCpp/Host/Linux/SystemFontResolver.cpp index e228a63..43bb6d4 100644 --- a/openVulkanoCpp/Host/Linux/SystemFontResolver.cpp +++ b/openVulkanoCpp/Host/Linux/SystemFontResolver.cpp @@ -39,8 +39,8 @@ namespace OpenVulkano { FcPattern* font = fs->fonts[i]; FcChar8* file; - FcChar8 *style; - FcChar8 *family; + FcChar8* style; + FcChar8* family; if (FcPatternGetString(font, FC_FILE, 0, &file) == FcResultMatch && FcPatternGetString(font, FC_FAMILY, 0, &family) == FcResultMatch && FcPatternGetString(font, FC_STYLE, 0, &style) == FcResultMatch) diff --git a/openVulkanoCpp/Host/Windows/SystemFontResolver.cpp b/openVulkanoCpp/Host/Windows/SystemFontResolver.cpp index 516d2fd..c30d16d 100644 --- a/openVulkanoCpp/Host/Windows/SystemFontResolver.cpp +++ b/openVulkanoCpp/Host/Windows/SystemFontResolver.cpp @@ -49,14 +49,7 @@ namespace OpenVulkano { // remove null-terminated characters since size is always bigger than needed std::string fontNameCropped(fontName.begin(), fontName.end()); - size_t realSize = 0; - for (; realSize < fontNameCropped.size(); realSize++) - { - if (fontNameCropped[realSize] == '\0') - { - break; - } - } + size_t realSize = strlen(fontNameCropped.data()); fontNameCropped.resize(realSize); Utils::ToLower(fontNameCropped); fontFileMapping[std::move(fontNameCropped)] = fontFilename.path().string(); From 4442d1b0fcf1c4965ee2a456501371af85bf47c6 Mon Sep 17 00:00:00 2001 From: ohyzha Date: Tue, 21 Jan 2025 11:54:58 +0200 Subject: [PATCH 15/15] use direct write to get system fonts --- .../Host/Windows/SystemFontResolver.cpp | 105 ++++++++++++++++-- tests/Host/Windows/SystemFontsSearchTests.cpp | 6 +- 2 files changed, 96 insertions(+), 15 deletions(-) diff --git a/openVulkanoCpp/Host/Windows/SystemFontResolver.cpp b/openVulkanoCpp/Host/Windows/SystemFontResolver.cpp index c30d16d..111cc00 100644 --- a/openVulkanoCpp/Host/Windows/SystemFontResolver.cpp +++ b/openVulkanoCpp/Host/Windows/SystemFontResolver.cpp @@ -6,11 +6,35 @@ #include "Host/SystemFontResolver.hpp" #include "Base/Utils.hpp" +#include "Base/Logger.hpp" #include +#include #include #define QFR_DESCRIPTION 1 +#pragma comment(lib, "Dwrite.lib") + +namespace +{ + template + struct DirectWriteAutoReleasable final + { + DirectWriteAutoReleasable() : ptr(nullptr) {} + ~DirectWriteAutoReleasable() + { + if (ptr) + { + ptr->Release(); + } + } + + T* operator->() { return ptr; } + + T* ptr; + }; +} + namespace OpenVulkano { const std::string& SystemFontResolver::GetSystemFontPath(const std::string& fontName) @@ -24,35 +48,92 @@ namespace OpenVulkano std::map SystemFontResolver::ReadSystemFonts() { + DirectWriteAutoReleasable dwrite; + HRESULT hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory7), (IUnknown**) &dwrite); + if (!SUCCEEDED(hr)) + { + Logger::DATA->error("Could not read system fonts. DWriteCreateFactory has failed. Error code {}", hr); + return {}; + } + + DirectWriteAutoReleasable matchingFonts; + hr = dwrite->GetSystemFontSet(&matchingFonts.ptr); + if (!SUCCEEDED(hr)) + { + Logger::DATA->error("Could not read system fonts. GetSystemFontSet has failed. Error code {}", hr); + return {}; + } + std::map fontFileMapping; + const UINT32 familyCount = matchingFonts->GetFontCount(); // thank you Microsoft for function that is not even documented, but exists and it's the only function that // can return real font name from font filename (e.g. font filename is times.ttf that corresponds to Times New Roman) int(WINAPI* GetFontResourceInfoW)(wchar_t*, unsigned long*, void*, unsigned long); *(FARPROC*) &GetFontResourceInfoW = GetProcAddress(GetModuleHandleA("gdi32"), "GetFontResourceInfoW"); - std::wstring winDir; - winDir.resize(MAX_PATH); - UINT len = GetWindowsDirectoryW(winDir.data(), MAX_PATH); - winDir.resize(len); - std::filesystem::path fontsDir = std::filesystem::path(winDir) / "Fonts"; - for (const auto& fontFilename : std::filesystem::directory_iterator(fontsDir)) + for (UINT32 i = 0; i < familyCount; ++i) { - unsigned long size = 0; - std::wstring ws = fontFilename.path().wstring(); - if (!GetFontResourceInfoW(ws.data(), &size, NULL, QFR_DESCRIPTION)) + DirectWriteAutoReleasable faceRef; + hr = matchingFonts->GetFontFaceReference(i, &faceRef.ptr); + if (!SUCCEEDED(hr)) { continue; } + + DirectWriteAutoReleasable file; + hr = faceRef->GetFontFile(&file.ptr); + if (!SUCCEEDED(hr)) + { + continue; + } + + DirectWriteAutoReleasable loader; + hr = file->GetLoader(&loader.ptr); + if (!SUCCEEDED(hr)) + { + continue; + } + + DirectWriteAutoReleasable localLoader; + hr = loader->QueryInterface(&localLoader.ptr); + if (!SUCCEEDED(hr)) + { + continue; + } + + const void* fileKey; + UINT32 fileKeySize; + hr = file->GetReferenceKey(&fileKey, &fileKeySize); + if (!SUCCEEDED(hr)) + { + continue; + } + + // Get font path + WCHAR filePath[MAX_PATH]; + hr = localLoader->GetFilePathFromKey(fileKey, fileKeySize, filePath, MAX_PATH); + if (!SUCCEEDED(hr)) + { + continue; + } + + // Get font name + unsigned long size = 0; + if (!GetFontResourceInfoW(filePath, &size, NULL, QFR_DESCRIPTION)) + { + continue; + } + std::wstring fontName; fontName.resize(size); - if (GetFontResourceInfoW(ws.data(), &size, fontName.data(), QFR_DESCRIPTION)) + if (GetFontResourceInfoW(filePath, &size, fontName.data(), QFR_DESCRIPTION)) { // remove null-terminated characters since size is always bigger than needed std::string fontNameCropped(fontName.begin(), fontName.end()); - size_t realSize = strlen(fontNameCropped.data()); + const size_t realSize = strlen(fontNameCropped.data()); fontNameCropped.resize(realSize); Utils::ToLower(fontNameCropped); - fontFileMapping[std::move(fontNameCropped)] = fontFilename.path().string(); + fontFileMapping[std::move(fontNameCropped)] = std::string(filePath, filePath + wcslen(filePath)); } } return fontFileMapping; diff --git a/tests/Host/Windows/SystemFontsSearchTests.cpp b/tests/Host/Windows/SystemFontsSearchTests.cpp index e478e57..c97c2a2 100644 --- a/tests/Host/Windows/SystemFontsSearchTests.cpp +++ b/tests/Host/Windows/SystemFontsSearchTests.cpp @@ -19,14 +19,14 @@ TEST_CASE("Search system fonts") // assume these fonts are present since they are default std::filesystem::path path = SystemFontResolver::GetSystemFontPath("Arial"); - REQUIRE(path.filename() == "arial.ttf"); + REQUIRE(path.filename() == "ARIAL.TTF"); // assume these fonts are present since they are default path = SystemFontResolver::GetSystemFontPath("Times New Roman"); - REQUIRE(path.filename() == "times.ttf"); + REQUIRE(path.filename() == "TIMES.TTF"); path = SystemFontResolver::GetSystemFontPath("Arial Bold Italic"); - REQUIRE(path.filename() == "arialbi.ttf"); + REQUIRE(path.filename() == "ARIALBI.TTF"); path = SystemFontResolver::GetSystemFontPath("NON-EXISTING Font"); REQUIRE(path.empty());