Merge pull request #1591 from ONLYOFFICE/feature/svg-font

Added custom SVG font
This commit is contained in:
Kirill Polyakov
2024-07-09 18:48:00 +03:00
committed by GitHub
16 changed files with 516 additions and 243 deletions

View File

@ -1,6 +1,7 @@
#include "CSvgFile.h"
#include "SvgObjects/CContainer.h"
#include "SvgObjects/CFont.h"
#define SVG_FILE_WIDTH 300
#define SVG_FILE_HEIGHT 150
@ -132,6 +133,16 @@ SVG::CObject *CSvgFile::GetMarkedObject(const std::wstring &wsId) const
return NULL;
}
SVG::CFont *CSvgFile::GetFont(const std::wstring &wsFontFamily) const
{
FontsFaceMap::const_iterator itFound = std::find_if(m_mFontsFace.cbegin(), m_mFontsFace.cend(), [&wsFontFamily](const std::pair<std::wstring, std::wstring>& oValue){ return wsFontFamily == oValue.first; });
if (m_mFontsFace.cend() == itFound)
return NULL;
return dynamic_cast<SVG::CFont*>(GetMarkedObject(itFound->second));
}
std::wstring CSvgFile::GetWorkingDirectory() const
{
return m_wsWorkingDirectory;
@ -142,6 +153,11 @@ void CSvgFile::AddStyles(const std::wstring &wsStyles)
m_oSvgCalculator.AddStyles(wsStyles);
}
void CSvgFile::AddFontFace(const SVG::TFontArguments& oArguments, const std::wstring &wsId)
{
m_mFontsFace.insert(std::make_pair(oArguments.m_wsFontFamily, wsId));
}
bool CSvgFile::Draw(IRenderer *pRenderer, double dX, double dY, double dWidth, double dHeight)
{
if (NULL == pRenderer || m_oContainer.Empty())

View File

@ -9,6 +9,12 @@
#define SVG_DECL_IMPORT Q_DECL_IMPORT
namespace SVG
{
struct TFontArguments;
class CFont;
}
class SVG_DECL_IMPORT CSvgFile
{
public:
@ -28,9 +34,12 @@ class SVG_DECL_IMPORT CSvgFile
bool MarkObject(SVG::CObject* pObject);
SVG::CObject* GetMarkedObject(const std::wstring& wsId) const;
SVG::CFont* GetFont(const std::wstring& wsFontFamily) const;
std::wstring GetWorkingDirectory() const;
void AddStyles(const std::wstring& wsStyles);
void AddFontFace(const SVG::TFontArguments& oArguments, const std::wstring& wsId);
bool Draw(IRenderer* pRenderer, double dX, double dY, double dWidth, double dHeight);
private:
@ -44,6 +53,9 @@ class SVG_DECL_IMPORT CSvgFile
MarkedMap m_mMarkedObjects;
std::wstring m_wsWorkingDirectory;
typedef std::map<std::wstring, std::wstring> FontsFaceMap;
FontsFaceMap m_mFontsFace;
};
#endif // CSVGFILE_H

View File

@ -6,7 +6,6 @@
#include "CSvgFile.h"
#include "SvgObjects/CContainer.h"
#include "SvgObjects/CPolyline.h"
#include "SvgObjects/CGradient.h"
#include "SvgObjects/CClipPath.h"
@ -21,6 +20,7 @@
#include "SvgObjects/CRect.h"
#include "SvgObjects/CLine.h"
#include "SvgObjects/CPath.h"
#include "SvgObjects/CFont.h"
#include "SvgObjects/CText.h"
#include "SvgObjects/CMask.h"
#include "SvgObjects/CUse.h"
@ -95,7 +95,7 @@ namespace SVG
if (L"style" == wsElementName)
{
pFile->AddStyles(oElement.GetText());
ParseStyles(oElement.GetText(), pFile);
return true;
}
@ -116,6 +116,61 @@ namespace SVG
return bScanResult;
}
void CSvgParser::ParseStyles(const std::wstring &wsStyles, CSvgFile *pFile) const
{
if (NULL == pFile)
return;
pFile->AddStyles(wsStyles);
std::wregex oRegex(L"@font-face\\s*(\\{[^}]*\\})");
std::wsmatch oMatch;
std::wstring::const_iterator oSearchStart(wsStyles.cbegin());
while (std::regex_search(oSearchStart, wsStyles.cend(), oMatch, oRegex))
{
if (oMatch[1].str().empty())
continue;
std::wstring wsValue{oMatch[1].str()};
std::wstring::const_iterator itStart = std::find_if(wsValue.cbegin(), wsValue.cend(), [](const wchar_t& wChar) { return !std::iswspace(wChar) && L'{' != wChar; });
std::wstring::const_reverse_iterator itEnd = std::find_if(wsValue.crbegin(), wsValue.crend(), [](const wchar_t& wChar) { return !std::iswspace(wChar) && L'}' != wChar; });
if (wsValue.cend() != itStart && wsValue.crend() != itEnd)
wsValue = std::wstring(itStart, itEnd.base());
const std::vector<std::wstring> arWords{NSCSS::NS_STATIC_FUNCTIONS::GetWordsW(wsValue, true, L":;")};
SvgURL oURL;
TFontArguments m_oArguments;
for (unsigned int unIndex = 0; unIndex < arWords.size(); ++unIndex)
{
if (arWords[unIndex].length() > 3 && L"src" == arWords[unIndex].substr(0, 3) && L':' == arWords[unIndex].back() &&
unIndex + 1 < arWords.size() && oURL.SetValue(arWords[++unIndex]))
{
continue;
}
else if (arWords[unIndex].length() > 11 && L"font-family" == arWords[unIndex].substr(0, 11) && L':' == arWords[unIndex].back() &&
unIndex + 1 < arWords.size())
{
const std::vector<std::wstring> arFontFamily{NSCSS::NS_STATIC_FUNCTIONS::GetWordsW(arWords[++unIndex], false, L"\"\',;")};
if (arFontFamily.empty())
continue;
m_oArguments.m_wsFontFamily = arFontFamily.back();
}
}
if (!oURL.Empty() && !m_oArguments.m_wsFontFamily.empty())
pFile->AddFontFace(m_oArguments, oURL.GetValue());
oSearchStart = oMatch.suffix().first;
}
}
template <class ObjectType>
bool CSvgParser::ReadObject(XmlUtils::CXmlNode &oElement, CContainer<ObjectType> *pContainer, CSvgFile *pFile, CRenderedObject *pParent) const
@ -229,6 +284,10 @@ namespace SVG
else
RELEASEOBJECT(pObject);
}
else if (L"font" == wsElementName)
{
pObject = new CFont(oElement);
}
if (NULL != pObject)
{

View File

@ -2,6 +2,7 @@
#define CSVGPARSER_H
#include "../../../common/Directory.h"
#include "../../../graphics/pro/Fonts.h"
#include "../../../xml/include/xmlutils.h"
#include "SvgObjects/CContainer.h"
@ -29,6 +30,7 @@ namespace SVG
bool ReadChildrens(XmlUtils::CXmlNode& oElement, CContainer<ObjectType>* pContainer, CSvgFile* pFile, CRenderedObject* pParent = NULL) const;
bool ScanStyles(XmlUtils::CXmlNode& oElement, CSvgFile* pFile) const;
void ParseStyles(const std::wstring& wsStyles, CSvgFile *pFile) const;
void UpdateStyles(CObject* pObject, CSvgFile* pFile) const;
bool MarkObject(CObject* pObject, CSvgFile* pFile) const;

View File

@ -3,8 +3,6 @@
#include "CObjectBase.h"
#include "../../../../graphics/pro/Fonts.h"
class CSvgFile;
namespace SVG

View File

@ -0,0 +1,143 @@
#include "CFont.h"
namespace SVG
{
CGlyph::CGlyph(XmlUtils::CXmlNode &oNode)
: CPath(oNode)
{
std::wstring wsUnicode(oNode.GetAttribute(L"unicode"));
if (!wsUnicode.empty())
m_wchUnicode = wsUnicode[0];
m_oHorizAdvX.SetValue(oNode.GetAttributeOrValue(L"horiz-adv-x"));
}
wchar_t CGlyph::GetUnicode() const
{
return m_wchUnicode;
}
CFontFace::CFontFace(XmlUtils::CXmlNode &oNode)
{
}
CFont::CFont(XmlUtils::CXmlNode &oNode)
: CAppliedObject(oNode), m_pMissingGlyph(NULL)
{
ParseGlyphs(oNode);
m_oArguments.m_wsFontVariant = oNode.GetAttribute(L"font-variant");
m_oArguments.m_wsFontStyle = oNode.GetAttribute(L"font-style");
m_oArguments.m_wsFontWidght = oNode.GetAttribute(L"font-weight");
m_oHorizAdvX.SetValue(oNode.GetAttributeOrValue(L"horiz-adv-x"));
}
CFont::~CFont()
{
for (std::pair<wchar_t, CGlyph*> oElement : m_mGlyphs)
RELEASEOBJECT(oElement.second);
RELEASEOBJECT(m_pMissingGlyph);
}
void CFont::SetData(const std::map<std::wstring, std::wstring> &mAttributes, unsigned short ushLevel, bool bHardMode)
{
}
bool CFont::Apply(IRenderer *pRenderer, const CSvgFile *pFile, const TBounds &oObjectBounds)
{
return false;
}
bool CFont::Draw(const std::wstring &wsText, double dX, double dY, IRenderer *pRenderer, const CSvgFile *pFile, CommandeMode oMode, const TSvgStyles *pStyles) const
{
if (NULL == pRenderer)
return false;
double dM11, dM12, dM21, dM22, dRx, dRy;
pRenderer->GetTransform(&dM11, &dM12, &dM21, &dM22, &dRx, &dRy);
Aggplus::CMatrix oMatrix(dM11, dM12, dM21, dM22, dRx, dRy);
oMatrix.Translate(dX, dY);
pRenderer->SetTransform(dM11, dM12, dM21, -dM22, oMatrix.tx(), oMatrix.ty());
MGlyphsMap::const_iterator itFound;
TBounds oGlyphBounds;
for (wchar_t wchGlyph : wsText)
{
itFound = m_mGlyphs.find(wchGlyph);
if (m_mGlyphs.cend() == itFound)
{
if (NULL == m_pMissingGlyph)
continue;
m_pMissingGlyph->Draw(pRenderer, pFile, oMode, pStyles);
oMatrix.Translate(m_oHorizAdvX.ToDouble(NSCSS::Pixel), 0);
}
else
{
itFound->second->Draw(pRenderer, pFile, oMode, pStyles);
if (!itFound->second->m_oHorizAdvX.Empty())
oMatrix.Translate(itFound->second->m_oHorizAdvX.ToDouble(NSCSS::Pixel), 0);
else
oMatrix.Translate(m_oHorizAdvX.ToDouble(NSCSS::Pixel), 0);
}
oMatrix.Translate(std::abs(oGlyphBounds.m_dRight - oGlyphBounds.m_dLeft), 0);
pRenderer->SetTransform(dM11, dM12, dM21, -dM22, oMatrix.tx(), oMatrix.ty());
}
pRenderer->SetTransform(dM11, dM12, dM21, dM22, dRx, dRy);
return true;
}
void CFont::ParseGlyphs(XmlUtils::CXmlNode &oNode)
{
std::vector<XmlUtils::CXmlNode> arChilds;
if (!oNode.GetChilds(arChilds) || arChilds.empty())
return;
for (XmlUtils::CXmlNode& oChild : arChilds)
{
if (L"glyph" == oChild.GetName())
{
CGlyph *pGlyph = new CGlyph(oChild);
if (NULL == pGlyph)
continue;
if (m_mGlyphs.end() == m_mGlyphs.find(pGlyph->GetUnicode()))
m_mGlyphs.insert(std::make_pair(pGlyph->GetUnicode(), pGlyph));
else
delete pGlyph;
}
else if (L"missing-glyph" == oChild.GetName())
{
std::vector<XmlUtils::CXmlNode> arMissingGlyphChilds;
if (!oChild.GetChilds(arMissingGlyphChilds) || arMissingGlyphChilds.empty())
continue;
for (XmlUtils::CXmlNode& oChildMissingGlyph : arMissingGlyphChilds)
{
if (L"path" == oChildMissingGlyph.GetName())
{
m_pMissingGlyph = new CPath(oChildMissingGlyph);
if (NULL != m_pMissingGlyph)
break;
}
}
}
}
}
}

View File

@ -0,0 +1,61 @@
#ifndef CFONT_H
#define CFONT_H
#include "CPath.h"
namespace SVG
{
class CGlyph : public CPath
{
public:
CGlyph(XmlUtils::CXmlNode& oNode);
wchar_t GetUnicode() const;
private:
wchar_t m_wchUnicode;
SvgDigit m_oHorizAdvX;
friend class CFont;
};
class CFontFace
{
public:
CFontFace(XmlUtils::CXmlNode& oNode);
private:
std::wstring m_wsSrcFaceName;
};
struct TFontArguments
{
std::wstring m_wsFontFamily;
std::wstring m_wsFontVariant;
std::wstring m_wsFontStyle;
std::wstring m_wsFontWidght;
};
class CFont : public CAppliedObject
{
public:
CFont(XmlUtils::CXmlNode& oNode);
~CFont();
void SetData(const std::map<std::wstring, std::wstring> &mAttributes, unsigned short ushLevel, bool bHardMode) override;
bool Apply(IRenderer* pRenderer, const CSvgFile *pFile, const TBounds &oObjectBounds) override;
bool Draw(const std::wstring& wsText, double dX, double dY, IRenderer* pRenderer, const CSvgFile *pFile, CommandeMode oMode = CommandeModeDraw, const TSvgStyles* pStyles = NULL) const;
private:
void ParseGlyphs(XmlUtils::CXmlNode& oNode);
TFontArguments m_oArguments;
typedef std::map<wchar_t, CGlyph*> MGlyphsMap;
MGlyphsMap m_mGlyphs;
CPath *m_pMissingGlyph;
SvgDigit m_oHorizAdvX;
};
}
#endif // CFONT_H

View File

@ -2,6 +2,7 @@
#define CPATTERN_H
#include "CContainer.h"
#include "../../../../graphics/pro/Fonts.h"
namespace SVG
{

View File

@ -2,6 +2,7 @@
#define CSYMBOL_H
#include "CContainer.h"
#include "../../../graphics/pro/Fonts.h"
namespace SVG
{

View File

@ -4,6 +4,7 @@
#include "../SvgUtils.h"
#include "../CSvgFile.h"
#include "CContainer.h"
#include "CFont.h"
#include "CStyle.h"
#ifndef MININT8
@ -152,12 +153,15 @@ namespace SVG
double dX, dY;
CalculatePosition(dX, dY);
ApplyFont(pRenderer, dX, dY);
pRenderer->CommandDrawText(m_wsText, dX, dY, 0, 0);
for (const CRenderedObject* pTSpan : m_arObjects)
pTSpan->Draw(pRenderer, pFile, oMode, pOtherStyles);
if (!UseExternalFont(pFile, dX, dY, pRenderer, pFile, oMode, pOtherStyles))
{
ApplyFont(pRenderer, dX, dY);
pRenderer->CommandDrawText(m_wsText, dX, dY, 0, 0);
for (const CRenderedObject* pTSpan : m_arObjects)
pTSpan->Draw(pRenderer, pFile, oMode, pOtherStyles);
}
EndPath(pRenderer, pFile, oOldMatrix, oMode, pOtherStyles);
@ -292,6 +296,26 @@ namespace SVG
pRenderer->put_BrushColor1(m_oStyles.m_oFill.ToInt());
pRenderer->put_BrushAlpha1(255);
}
bool CTSpan::UseExternalFont(const CSvgFile *pFile, double dX, double dY, IRenderer *pRenderer, const CSvgFile *pFile, CommandeMode oMode, const TSvgStyles *pOtherStyles) const
{
std::wstring wsFontFamily = DefaultFontFamily;
if (!m_oFont.GetFamily().Empty())
{
wsFontFamily = m_oFont.GetFamily().ToWString();
CorrectFontFamily(wsFontFamily);
}
CFont *pFont = pFile->GetFont(wsFontFamily);
if (NULL == pFont)
return false;
pFont->Draw(m_wsText, dX, dY, pRenderer, pFile, oMode, pOtherStyles);
return true;
}
TBounds CTSpan::GetBounds() const
{

View File

@ -31,6 +31,8 @@ namespace SVG
void ApplyStyle(IRenderer* pRenderer, const TSvgStyles* pStyles, const CSvgFile* pFile, int& nTypePath) const override;
void ApplyFont(IRenderer* pRenderer, double& dX, double& dY) const;
bool UseExternalFont(const CSvgFile* pFile, double dX, double dY, IRenderer* pRenderer, const CSvgFile* pFile, CommandeMode oMode = CommandeModeDraw, const TSvgStyles* pOtherStyles = NULL) const;
TBounds GetBounds() const override;
double GetWidth() const;

View File

@ -14,6 +14,7 @@ namespace SVG
#define SvgDigit NSCSS::NSProperties::CDigit
#define SvgString NSCSS::NSProperties::CString
#define SvgColor NSCSS::NSProperties::CColor
#define SvgURL NSCSS::NSProperties::CURL
#define SvgEnum NSCSS::NSProperties::CEnum
#define SvgTransform NSCSS::NSProperties::CTransform