From 4442d1b0fcf1c4965ee2a456501371af85bf47c6 Mon Sep 17 00:00:00 2001 From: ohyzha Date: Tue, 21 Jan 2025 11:54:58 +0200 Subject: [PATCH] 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());