Skip to content
Draft
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
8a11b3c
mbedTLS fix for cURL 8.8.0
Lpsd May 24, 2024
181bf0f
Merge branch 'master' of https://github.com/multitheftauto/mtasa-blue
Lpsd May 24, 2024
6728cbe
Merge branch 'master' of https://github.com/multitheftauto/mtasa-blue
Lpsd May 25, 2024
0b989ac
Merge branch 'master' of https://github.com/multitheftauto/mtasa-blue
Lpsd May 26, 2024
9c19509
Merge branch 'master' of https://github.com/multitheftauto/mtasa-blue
Lpsd Jun 2, 2024
75c36ed
Merge branch 'master' of https://github.com/multitheftauto/mtasa-blue
Lpsd Jun 19, 2024
96dd1ee
Merge branch 'master' of https://github.com/multitheftauto/mtasa-blue
Lpsd Oct 2, 2024
f9eaf3f
Merge branch 'master' of https://github.com/multitheftauto/mtasa-blue
Lpsd Oct 8, 2024
54bd11b
Merge branch 'master' of https://github.com/multitheftauto/mtasa-blue
Lpsd Oct 10, 2024
ddee2d0
Merge branch 'master' of https://github.com/multitheftauto/mtasa-blue
Lpsd Oct 19, 2024
288b8db
Merge branch 'master' of https://github.com/multitheftauto/mtasa-blue
Lpsd Oct 21, 2024
b30a4a2
Merge branch 'master' of https://github.com/multitheftauto/mtasa-blue
Lpsd Oct 22, 2024
27b9eae
Merge branch 'master' of https://github.com/multitheftauto/mtasa-blue
Lpsd Dec 17, 2024
3731d81
Merge branch 'master' of https://github.com/multitheftauto/mtasa-blue
Lpsd Jan 14, 2025
b50a68c
Merge branch 'master' of https://github.com/multitheftauto/mtasa-blue
Lpsd Jan 22, 2025
58ae23f
Merge branch 'master' of https://github.com/multitheftauto/mtasa-blue
Lpsd Feb 19, 2025
f3645b8
Merge branch 'master' of https://github.com/multitheftauto/mtasa-blue
Lpsd Mar 4, 2025
e62d9f4
Merge branch 'master' of https://github.com/multitheftauto/mtasa-blue
Lpsd Mar 14, 2025
3346e9c
Merge branch 'master' of https://github.com/multitheftauto/mtasa-blue
Lpsd Mar 16, 2025
95dfc97
Merge branch 'master' of https://github.com/multitheftauto/mtasa-blue
Lpsd Sep 6, 2025
3307dc6
Merge branch 'master' of https://github.com/multitheftauto/mtasa-blue
Lpsd Sep 14, 2025
f091a85
Merge branch 'master' of https://github.com/multitheftauto/mtasa-blue
Lpsd Sep 24, 2025
c66e5de
Merge branch 'master' of https://github.com/multitheftauto/mtasa-blue
Lpsd Nov 7, 2025
c132ae1
Merge branch 'master' of https://github.com/multitheftauto/mtasa-blue
Lpsd Nov 9, 2025
5739ef0
Merge branch 'master' of https://github.com/multitheftauto/mtasa-blue
Lpsd Jan 7, 2026
41f7bfa
Update lunasvg vendor to 3.5.0
Lpsd Jan 7, 2026
3a613f2
Update premake5.lua
Lpsd Jan 7, 2026
969dfec
Allow custom resource fonts
Lpsd Jan 7, 2026
892cc22
Merge branch 'master' into lunasvg-3.5.0
Lpsd Jan 7, 2026
7c29995
Revert "Allow custom resource fonts"
Lpsd Jan 8, 2026
fbfd66c
Re-introduce sanity checks
Lpsd Jan 8, 2026
3f16442
Reapply "Allow custom resource fonts"
Lpsd Jan 8, 2026
c37786d
Merge branch 'master' into lunasvg-fonts
Lpsd Jan 8, 2026
0cc6405
Add check for existing font on register
Lpsd Jan 8, 2026
6874010
Avoid memcpy in font registration
Lpsd Jan 8, 2026
9fae239
Use key view in GetRegisteredFonts
Lpsd Jan 8, 2026
4cbba65
Use map.contains in IsFontRegistered
Lpsd Jan 8, 2026
96e8601
Update comments (todo)
Lpsd Jan 8, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Client/mods/deathmatch/logic/CClientVectorGraphic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ bool CClientVectorGraphic::SetDocument(CXMLNode* node)
m_pXMLDocument = node;
m_pSVGDocument = lunasvg::Document::loadFromData(node->ToString());

// Check if LunaSVG successfully parsed the document
if (!m_pSVGDocument)
return false;

m_pVectorGraphicDisplay->Update();

return true;
Expand Down
4 changes: 4 additions & 0 deletions Client/mods/deathmatch/logic/CClientVectorGraphicDisplay.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@ void CClientVectorGraphicDisplay::UpdateTexture()
if (!surface)
return;

// Check for valid SVG dimensions to avoid division by zero
if (svgDocument->width() <= 0 || svgDocument->height() <= 0)
return;

// SVG has a predefined width and height. We need transform it to the requested size
const Matrix transformationMatrix(pVectorGraphicItem->m_uiSizeX / svgDocument->width(), 0, 0, pVectorGraphicItem->m_uiSizeY / svgDocument->height(), 0, 0);

Expand Down
4 changes: 4 additions & 0 deletions Client/mods/deathmatch/logic/CResource.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#define DECLARE_PROFILER_SECTION_CResource
#include "profiler/SharedUtil.Profiler.h"
#include "CServerIdManager.h"
#include "CSVGFontManager.h"

using namespace std;

Expand Down Expand Up @@ -94,6 +95,9 @@ CResource::CResource(unsigned short usNetID, const char* szResourceName, CClient

CResource::~CResource()
{
// Unregister SVG fonts registered by this resource
CSVGFontManager::GetSingleton().UnregisterResourceFonts(this);

// Remove refrences from requested models
m_modelStreamer.ReleaseAll();

Expand Down
119 changes: 119 additions & 0 deletions Client/mods/deathmatch/logic/CSVGFontManager.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/*****************************************************************************
*
* 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>

CSVGFontManager& CSVGFontManager::GetSingleton()
{
static CSVGFontManager instance;
return instance;
}

void CSVGFontManager::FontDataDestroyCallback(void* pData)
{
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 (m_RegisteredFonts.find(strFontFamily) != m_RegisteredFonts.end())
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;

SString fontData;
if (!FileLoad(strAbsPath, fontData))
return false;

if (fontData.empty())
return false;

// Allocate memory for font data that will be owned by LunaSVG
size_t dataSize = fontData.size();
char* pFontData = new char[dataSize];
memcpy(pFontData, fontData.data(), dataSize);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a way we can avoid the memcpy? e.g. FileLoad straight into a char* or cast SString to a char*?

I don't know what the recommended approach is here, but avoiding the memcpy seems preferable

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FileLoad uses SString which manages its own memory, so we can't pass this to LunaSVG for it to take ownership (and there is no way to 'release' the buffer provided by FileLoad)...

However, we can use the SharedUtil file functions for this to avoid the memcpy, which I've changed.


// Register font with LunaSVG using data API
// LunaSVG takes ownership of the data and will call our destroy callback when done
if (!lunasvg_add_font_face_from_data(strFontFamily.c_str(), false, false, pFontData, dataSize, FontDataDestroyCallback, pFontData))
Copy link
Copy Markdown
Member

@qaisjp qaisjp Jan 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Relationship between resource lifetime and fonts in m_RegisteredFonts are clear so far, but it looks like there could be some sort of desync between what's in m_RegisteredFonts and what's actually in lunasvg.

my questions are:

  1. In what scenario is FontDataDestroyCallback called?
  2. Does the callback also need to remove the font from m_RegisteredFonts? (I don't know if it's possible for lunasvg to eagerly unload a font?)
  3. UnregisterFont (on resource unload) doesn't communicate with lunasvg. Should it?

wdyt?

Copy link
Copy Markdown
Member Author

@Lpsd Lpsd Jan 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking into this further, it doesn't actually work how I thought. Once we register a font with lunasvg/plutovg, the reference count on that side will always remain above 0, and therefore our callback won't ever be fired (see plutovg_font_face_destroy)

There's no way to request removal of font faces from cache using the public API (i.e no such lunasvg_remove_font_face), the only font related public APIs are lunasvg_add_font_face_from_file and lunasvg_add_font_face_from_data. Therefore once registered, a font will remain in the lunasvg/plutovg cache until application shutdown.

We'll need to defer this PR until the public API for lifecycle of font faces is improved, particularly around explicit destruction (I'm considering opening a PR @ lunasvg repo to speed this up).

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

{
// 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;

// Remove from our tracking
// Note: The font data memory will be freed by LunaSVG via our destroy callback
m_RegisteredFonts.erase(it);
return true;
}

bool CSVGFontManager::IsFontRegistered(const SString& strFontFamily) const
{
return m_RegisteredFonts.find(strFontFamily) != m_RegisteredFonts.end();
Copy link
Copy Markdown
Member

@qaisjp qaisjp Jan 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this invalid?

Suggested change
return m_RegisteredFonts.find(strFontFamily) != m_RegisteredFonts.end();
return m_RegisteredFonts.contains(strFontFamily);

https://en.cppreference.com/w/cpp/container/map/contains.html

If it's valid, I think there's opportunity to use this function elsewhere in your PR too (RegisterFont)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is indeed valid - updated!

}

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)
m_RegisteredFonts.erase(fontFamily);
}

std::vector<SString> CSVGFontManager::GetRegisteredFonts() const
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GetRegisteredFonts is never called

{
std::vector<SString> fonts;
fonts.reserve(m_RegisteredFonts.size());

for (const auto& pair : m_RegisteredFonts)
fonts.push_back(pair.first);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Non-blocking feedback) The code you have is good because it's very clear what it does. Apparently in C++20 there's now a two-liner for this, up to you whether you want to try it: https://stackoverflow.com/a/68094571/1517394

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implemented


return fonts;
}
48 changes: 48 additions & 0 deletions Client/mods/deathmatch/logic/CSVGFontManager.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*****************************************************************************
*
* 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;
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;
};
31 changes: 30 additions & 1 deletion Client/mods/deathmatch/logic/luadefs/CLuaVectorGraphicDefs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "StdInc.h"
#include "lua/CLuaFunctionParser.h"
#include "CLuaVectorGraphicDefs.h"
#include "../CSVGFontManager.h"

void CLuaVectorGraphicDefs::LoadFunctions()
{
Expand All @@ -20,7 +21,8 @@ void CLuaVectorGraphicDefs::LoadFunctions()
{"svgGetSize", ArgumentParser<SVGGetSize>},
{"svgSetSize", ArgumentParser<SVGSetSize>},
{"svgSetUpdateCallback", ArgumentParser<SVGSetUpdateCallback>},

{"svgRegisterFont", ArgumentParser<SVGRegisterFont>},
{"svgUnregisterFont", ArgumentParser<SVGUnregisterFont>},
};

// Add functions
Expand Down Expand Up @@ -271,3 +273,30 @@ 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;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The 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 false.

Bonus points if the warning message includes the name of the resource that registered the font

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implemented

return CSVGFontManager::GetSingleton().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);
}
4 changes: 4 additions & 0 deletions Client/mods/deathmatch/logic/luadefs/CLuaVectorGraphicDefs.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ class CLuaVectorGraphicDefs : public CLuaDefs
static CLuaMultiReturn<int, int> SVGGetSize(CClientVectorGraphic* vectorGraphic);
static bool SVGSetSize(CClientVectorGraphic* vectorGraphic, CVector2D size, std::optional<CLuaFunctionRef> luaFunctionRef);

// Font management functions
static bool SVGRegisterFont(lua_State* luaVM, std::string fontFamily, std::string fontPath);
static bool SVGUnregisterFont(std::string fontFamily);

private:
static bool LoadFromData(lua_State* luaVM, CClientVectorGraphic* vectorGraphic, std::string rawData);
static bool LoadFromFile(lua_State* luaVM, CClientVectorGraphic* vectorGraphic, CScriptFile* file, std::string path, CResource* parentResource);
Expand Down
Loading
Loading