mirror of
https://github.com/ONLYOFFICE/core.git
synced 2026-04-07 13:55:33 +08:00
Merge pull request #1591 from ONLYOFFICE/feature/svg-font
Added custom SVG font
This commit is contained in:
@ -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())
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
{
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -3,8 +3,6 @@
|
||||
|
||||
#include "CObjectBase.h"
|
||||
|
||||
#include "../../../../graphics/pro/Fonts.h"
|
||||
|
||||
class CSvgFile;
|
||||
|
||||
namespace SVG
|
||||
|
||||
143
DesktopEditor/raster/Metafile/svg/SvgObjects/CFont.cpp
Normal file
143
DesktopEditor/raster/Metafile/svg/SvgObjects/CFont.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
61
DesktopEditor/raster/Metafile/svg/SvgObjects/CFont.h
Normal file
61
DesktopEditor/raster/Metafile/svg/SvgObjects/CFont.h
Normal 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
|
||||
@ -2,6 +2,7 @@
|
||||
#define CPATTERN_H
|
||||
|
||||
#include "CContainer.h"
|
||||
#include "../../../../graphics/pro/Fonts.h"
|
||||
|
||||
namespace SVG
|
||||
{
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
#define CSYMBOL_H
|
||||
|
||||
#include "CContainer.h"
|
||||
#include "../../../graphics/pro/Fonts.h"
|
||||
|
||||
namespace SVG
|
||||
{
|
||||
|
||||
@ -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
|
||||
{
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
|
||||
Reference in New Issue
Block a user