/* * 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 "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 { namespace { const std::filesystem::path FALLBACK_PATH; } const std::filesystem::path& SystemFontResolver::GetSystemFontPath(const std::string& fontName) { static std::map fontFileMapping = ReadSystemFonts(); auto it = fontFileMapping.find(Utils::ToLower(fontName)); return it == fontFileMapping.end() ? FALLBACK_PATH : it->second; } 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"); for (UINT32 i = 0; i < familyCount; ++i) { 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(filePath, &size, fontName.data(), QFR_DESCRIPTION)) { // remove null-terminated characters since size is always bigger than needed std::string fontNameCropped(fontName.begin(), fontName.end()); const size_t realSize = strlen(fontNameCropped.data()); fontNameCropped.resize(realSize); Utils::ToLower(fontNameCropped); fontFileMapping[std::move(fontNameCropped)] = std::filesystem::path(std::wstring(filePath, filePath + wcslen(filePath))); } } return fontFileMapping; } }