-
-
Notifications
You must be signed in to change notification settings - Fork 529
LunaSVG: add custom font support #4620
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
8a11b3c
181bf0f
6728cbe
0b989ac
9c19509
75c36ed
96dd1ee
f9eaf3f
54bd11b
ddee2d0
288b8db
b30a4a2
27b9eae
3731d81
b50a68c
58ae23f
f3645b8
e62d9f4
3346e9c
95dfc97
3307dc6
f091a85
c66e5de
c132ae1
5739ef0
41f7bfa
3a613f2
969dfec
892cc22
7c29995
fbfd66c
3f16442
c37786d
0cc6405
6874010
9fae239
4cbba65
96e8601
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,139 @@ | ||
| /***************************************************************************** | ||
| * | ||
| * PROJECT: Multi Theft Auto | ||
| * LICENSE: See LICENSE in the top level directory | ||
| * | ||
| * Multi Theft Auto is available from https://www.multitheftauto.com/ | ||
| * | ||
| *****************************************************************************/ | ||
|
|
||
| #include "StdInc.h" | ||
| #include "CSVGFontManager.h" | ||
| #include <lunasvg.h> | ||
| #include <ranges> | ||
|
|
||
| CSVGFontManager& CSVGFontManager::GetSingleton() | ||
| { | ||
| static CSVGFontManager instance; | ||
| return instance; | ||
| } | ||
|
|
||
| void CSVGFontManager::FontDataDestroyCallback(void* pData) | ||
| { | ||
| // We don't need to worry about our own tracking here (already handled), just free the memory | ||
| delete[] static_cast<char*>(pData); | ||
| } | ||
|
|
||
| bool CSVGFontManager::RegisterFont(const SString& strFontFamily, const SString& strFontPath, CResource* pResource) | ||
| { | ||
| if (strFontFamily.empty() || strFontPath.empty() || !pResource) | ||
| return false; | ||
|
|
||
| if (IsFontRegistered(strFontFamily)) | ||
| return false; | ||
|
|
||
| SString strAbsPath; | ||
| SString strMetaPath; | ||
|
|
||
| CResource* pFileResource = pResource; | ||
| if (!g_pClientGame->GetResourceManager()->ParseResourcePathInput(strFontPath, pFileResource, &strAbsPath, &strMetaPath)) | ||
| return false; | ||
|
|
||
| if (!FileExists(strAbsPath)) | ||
| return false; | ||
|
|
||
| // Get file size | ||
| uint64 fileSize = FileSize(strAbsPath); | ||
| if (fileSize == 0 || fileSize > 10 * 1024 * 1024) // max 10MB | ||
| return false; | ||
|
|
||
| // Allocate memory for font data that will be owned by LunaSVG | ||
| size_t dataSize = static_cast<size_t>(fileSize); | ||
| char* pFontData = new (std::nothrow) char[dataSize]; | ||
| if (!pFontData) | ||
| return false; | ||
|
|
||
| // Read file directly into our allocated buffer | ||
| FILE* pFile = File::Fopen(strAbsPath, "rb"); | ||
| if (!pFile) | ||
| { | ||
| delete[] pFontData; | ||
| return false; | ||
| } | ||
|
|
||
| size_t bytesRead = fread(pFontData, 1, dataSize, pFile); | ||
| fclose(pFile); | ||
|
|
||
| if (bytesRead != dataSize) | ||
| { | ||
| delete[] pFontData; | ||
| return false; | ||
| } | ||
|
|
||
| // Register font with LunaSVG using data API | ||
| // LunaSVG takes ownership of the data, we provide a callback to implement destruct behaviour, and notify when we're done | ||
| if (!lunasvg_add_font_face_from_data(strFontFamily.c_str(), false, false, pFontData, dataSize, FontDataDestroyCallback, pFontData)) | ||
| { | ||
| // Registration failed, clean up the memory ourselves | ||
| delete[] pFontData; | ||
| return false; | ||
| } | ||
|
|
||
| // Store font info for tracking | ||
| SFontInfo fontInfo; | ||
| fontInfo.strOriginalPath = strFontPath; | ||
| fontInfo.strAbsolutePath = strAbsPath; | ||
| fontInfo.pOwnerResource = pResource; | ||
|
|
||
| m_RegisteredFonts[strFontFamily] = std::move(fontInfo); | ||
|
|
||
| return true; | ||
| } | ||
|
|
||
| bool CSVGFontManager::UnregisterFont(const SString& strFontFamily) | ||
| { | ||
| auto it = m_RegisteredFonts.find(strFontFamily); | ||
| if (it == m_RegisteredFonts.end()) | ||
| return false; | ||
|
|
||
| // TODO: unregister font face with lunasvg here (see https://github.com/sammycage/lunasvg/issues/258) | ||
| m_RegisteredFonts.erase(it); | ||
| return true; | ||
| } | ||
|
|
||
| bool CSVGFontManager::IsFontRegistered(const SString& strFontFamily) const | ||
| { | ||
| return m_RegisteredFonts.contains(strFontFamily); | ||
| } | ||
|
|
||
| CResource* CSVGFontManager::GetFontOwnerResource(const SString& strFontFamily) const | ||
| { | ||
| auto it = m_RegisteredFonts.find(strFontFamily); | ||
| if (it != m_RegisteredFonts.end()) | ||
| return it->second.pOwnerResource; | ||
| return nullptr; | ||
| } | ||
|
|
||
| void CSVGFontManager::UnregisterResourceFonts(CResource* pResource) | ||
| { | ||
| if (!pResource) | ||
| return; | ||
|
|
||
| // Collect fonts to remove | ||
| std::vector<SString> fontsToRemove; | ||
| for (const auto& pair : m_RegisteredFonts) | ||
| { | ||
| if (pair.second.pOwnerResource == pResource) | ||
| fontsToRemove.push_back(pair.first); | ||
| } | ||
|
|
||
| // Remove collected fonts from our tracking | ||
| for (const auto& fontFamily : fontsToRemove) | ||
| UnregisterFont(fontFamily); | ||
| } | ||
|
|
||
| std::vector<SString> CSVGFontManager::GetRegisteredFonts() const | ||
| { | ||
| auto keys = std::views::keys(m_RegisteredFonts); | ||
| return {keys.begin(), keys.end()}; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| /***************************************************************************** | ||
| * | ||
| * PROJECT: Multi Theft Auto | ||
| * LICENSE: See LICENSE in the top level directory | ||
| * | ||
| * Multi Theft Auto is available from https://www.multitheftauto.com/ | ||
| * | ||
| *****************************************************************************/ | ||
|
|
||
| #pragma once | ||
|
|
||
| #include <string> | ||
| #include <map> | ||
| #include <vector> | ||
|
|
||
| class CResource; | ||
|
|
||
| class CSVGFontManager | ||
| { | ||
| public: | ||
| struct SFontInfo | ||
| { | ||
| SString strOriginalPath; // Original resource path for debugging | ||
| SString strAbsolutePath; // Absolute path to font file | ||
| CResource* pOwnerResource; // Resource that registered this font | ||
| }; | ||
|
|
||
| static CSVGFontManager& GetSingleton(); | ||
|
|
||
|
|
||
| bool RegisterFont(const SString& strFontFamily, const SString& strFontPath, CResource* pResource); | ||
| bool UnregisterFont(const SString& strFontFamily); | ||
| bool IsFontRegistered(const SString& strFontFamily) const; | ||
| CResource* GetFontOwnerResource(const SString& strFontFamily) const; | ||
| void UnregisterResourceFonts(CResource* pResource); | ||
| std::vector<SString> GetRegisteredFonts() const; | ||
|
|
||
| private: | ||
| CSVGFontManager() = default; | ||
| ~CSVGFontManager() = default; | ||
|
|
||
| CSVGFontManager(const CSVGFontManager&) = delete; | ||
| CSVGFontManager& operator=(const CSVGFontManager&) = delete; | ||
|
|
||
| static void FontDataDestroyCallback(void* pData); | ||
|
|
||
| // Fonts we're currently tracking (owned by resources) | ||
| std::map<SString, SFontInfo> m_RegisteredFonts; | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,6 +10,7 @@ | |
| #include "StdInc.h" | ||
| #include "lua/CLuaFunctionParser.h" | ||
| #include "CLuaVectorGraphicDefs.h" | ||
| #include "../CSVGFontManager.h" | ||
|
|
||
| void CLuaVectorGraphicDefs::LoadFunctions() | ||
| { | ||
|
|
@@ -20,7 +21,8 @@ void CLuaVectorGraphicDefs::LoadFunctions() | |
| {"svgGetSize", ArgumentParser<SVGGetSize>}, | ||
| {"svgSetSize", ArgumentParser<SVGSetSize>}, | ||
| {"svgSetUpdateCallback", ArgumentParser<SVGSetUpdateCallback>}, | ||
|
|
||
| {"svgRegisterFont", ArgumentParser<SVGRegisterFont>}, | ||
| {"svgUnregisterFont", ArgumentParser<SVGUnregisterFont>}, | ||
| }; | ||
|
|
||
| // Add functions | ||
|
|
@@ -271,3 +273,46 @@ std::variant<CLuaFunctionRef, bool> CLuaVectorGraphicDefs::SVGGetUpdateCallback( | |
|
|
||
| return false; | ||
| } | ||
|
|
||
| bool CLuaVectorGraphicDefs::SVGRegisterFont(lua_State* luaVM, std::string fontFamily, std::string fontPath) | ||
| { | ||
| if (fontFamily.empty()) | ||
| throw std::invalid_argument("Font family name cannot be empty"); | ||
|
|
||
| if (fontPath.empty()) | ||
| throw std::invalid_argument("Font path cannot be empty"); | ||
|
|
||
| CLuaMain* pLuaMain = m_pLuaManager->GetVirtualMachine(luaVM); | ||
| if (!pLuaMain) | ||
| return false; | ||
|
|
||
| CResource* pResource = pLuaMain->GetResource(); | ||
| if (!pResource) | ||
| return false; | ||
|
|
||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe we should emit a warning when the font is registered already? I think right now it just returns Bonus points if the warning message includes the name of the resource that registered the font
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Implemented |
||
| // Check if the font is already registered | ||
| CSVGFontManager& fontManager = CSVGFontManager::GetSingleton(); | ||
| if (fontManager.IsFontRegistered(fontFamily)) | ||
| { | ||
| CResource* pOwnerResource = fontManager.GetFontOwnerResource(fontFamily); | ||
| if (pOwnerResource) | ||
| { | ||
| m_pScriptDebugging->LogWarning(luaVM, "Font '%s' is already registered by resource '%s'", fontFamily.c_str(), pOwnerResource->GetName()); | ||
| } | ||
| else | ||
| { | ||
| m_pScriptDebugging->LogWarning(luaVM, "Font '%s' is already registered", fontFamily.c_str()); | ||
| } | ||
| return false; | ||
| } | ||
|
|
||
| return fontManager.RegisterFont(fontFamily, fontPath, pResource); | ||
| } | ||
|
|
||
| bool CLuaVectorGraphicDefs::SVGUnregisterFont(std::string fontFamily) | ||
| { | ||
| if (fontFamily.empty()) | ||
| throw std::invalid_argument("Font family name cannot be empty"); | ||
|
|
||
| return CSVGFontManager::GetSingleton().UnregisterFont(fontFamily); | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
GetRegisteredFonts is never called