/* * InfiniPaint * Copyright (C) 2025-2026 Yousef Khadadeh * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "FontData.hpp" #include #include #include #include #ifdef __EMSCRIPTEN__ #include #elif __ANDROID__ #include #include #include #include #include #elif __APPLE__ #include #include #elif _WIN32 #include #include "WindowsFontData/CustomFontSetManager.h" DWriteCustomFontSets::CustomFontSetManager fontSetManagerWindows; #else #include #include #include #endif #include #include FontData::FontData() { #ifdef __EMSCRIPTEN__ defaultFontMgr = SkFontMgr_New_Custom_Directory("data/fonts"); #elif __ANDROID__ { std::vector> fontFiles; int globCount; std::filesystem::path fontFolderPath("data/fonts"); char** filesInPath = SDL_GlobDirectory(fontFolderPath.c_str(), nullptr, 0, &globCount); if(filesInPath) { for(int i = 0; i < globCount; i++) { std::filesystem::path filePath = fontFolderPath / std::filesystem::path(filesInPath[i]); SDL_PathInfo fileInfo; if(SDL_GetPathInfo(filePath.c_str(), &fileInfo) && fileInfo.type == SDL_PATHTYPE_FILE) { std::string strData = read_file_to_string(filePath); fontFiles.emplace_back(SkData::MakeWithCopy(strData.data(), strData.size())); } } SDL_free(filesInPath); defaultFontMgr = SkFontMgr_New_Custom_Data(SkSpan(fontFiles.data(), fontFiles.size())); } } localFontMgr = SkFontMgr_New_AndroidNDK(true, SkFontScanner_Make_FreeType()); #elif __APPLE__ for(const auto& dirEntry : std::filesystem::recursive_directory_iterator("data/fonts")) { if(dirEntry.is_regular_file()) { std::string urlStr = std::string(std::filesystem::canonical(dirEntry.path()).string()); CFURLRef fontURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, reinterpret_cast(urlStr.c_str()), urlStr.size(), false); CTFontManagerRegisterFontsForURL(fontURL, CTFontManagerScope::kCTFontManagerScopeProcess, nullptr); CFRelease(fontURL); } } defaultFontMgr = SkFontMgr_New_CoreText(nullptr); #elif _WIN32 localFontMgr = SkFontMgr_New_DirectWrite(); std::vector fontPaths; for(const auto& dirEntry : std::filesystem::recursive_directory_iterator(std::filesystem::path(L"data\\fonts"))) { if(dirEntry.is_regular_file()) fontPaths.emplace_back(dirEntry.path().wstring()); } fontSetManagerWindows.CreateFontSetUsingLocalFontFiles(fontPaths); fontSetManagerWindows.CreateFontCollectionFromFontSet(); IDWriteFactory* fac = fontSetManagerWindows.IDWriteFactory5_IsAvailable() ? fontSetManagerWindows.m_dwriteFactory5.Get() : fontSetManagerWindows.m_dwriteFactory3.Get(); defaultFontMgr = SkFontMgr_New_DirectWrite(fac, fontSetManagerWindows.m_customFontCollection.Get()); #else localFontMgr = SkFontMgr_New_FontConfig(nullptr, SkFontScanner_Make_FreeType()); defaultFontMgr = SkFontMgr_New_Custom_Directory("data/fonts"); #endif map["Roboto"] = defaultFontMgr->makeFromFile("data/fonts/Roboto-variable.ttf"); collection = sk_make_sp(); collection->setDefaultFontManager(defaultFontMgr, std::vector{SkString{"Roboto"}, SkString{"Noto Emoji"}, SkString{"Amiri"}}); if(localFontMgr) collection->setDynamicFontManager(localFontMgr); std::string s = "🙂"; const char* sPtr = s.data(); SkFontStyle defaultStyle; SkString defaultLocale("en"); auto defaultEmojiFallback = collection->defaultEmojiFallback(SkUTF::NextUTF8(&sPtr, s.c_str() + s.length()), defaultStyle, defaultLocale); fallbackOnEmoji = !defaultEmojiFallback; } const std::vector& FontData::get_default_font_families() const { static std::vector defaultFontFamilies; if(defaultFontFamilies.empty()) { defaultFontFamilies.emplace_back("Roboto"); if(fallbackOnEmoji) defaultFontFamilies.emplace_back("Noto Emoji"); defaultFontFamilies.emplace_back("Amiri"); } return defaultFontFamilies; } void FontData::push_default_font_families(std::vector& fontFamilies) const { fontFamilies.emplace_back("Roboto"); if(fallbackOnEmoji) fontFamilies.emplace_back("Noto Emoji"); fontFamilies.emplace_back("Amiri"); } FontData::~FontData() { } Vector2f get_str_font_bounds(const SkFont& font, const std::string& str) { SkFontMetrics metrics; font.getMetrics(&metrics); float nextText = font.measureText(str.data(), str.length(), SkTextEncoding::kUTF8, nullptr); return Vector2f{nextText, - metrics.fAscent + metrics.fDescent}; }