From dcea017b51e5fedaf33126a599512426e2a21150 Mon Sep 17 00:00:00 2001 From: Kirill Poljakov Date: Wed, 22 Mar 2023 21:58:47 +0300 Subject: [PATCH] Added character rendering on textPath in svg --- .../3dParty/html/css/src/StyleProperties.cpp | 5 +- Common/3dParty/html/css/src/StyleProperties.h | 5 +- .../raster/Metafile/svg/SvgObjects/CPath.cpp | 46 ++++++--- .../raster/Metafile/svg/SvgObjects/CPath.h | 1 + .../raster/Metafile/svg/SvgObjects/CText.cpp | 98 ++++++++++++++----- .../raster/Metafile/svg/SvgObjects/CText.h | 6 +- 6 files changed, 116 insertions(+), 45 deletions(-) diff --git a/Common/3dParty/html/css/src/StyleProperties.cpp b/Common/3dParty/html/css/src/StyleProperties.cpp index b1f9f2dd88..975a8fd11f 100644 --- a/Common/3dParty/html/css/src/StyleProperties.cpp +++ b/Common/3dParty/html/css/src/StyleProperties.cpp @@ -853,7 +853,7 @@ namespace NSCSS return wsValue; } - Aggplus::CMatrix CMatrix::GetFinalValue(const Aggplus::CMatrix* pPrevMatrix) const + Aggplus::CMatrix CMatrix::GetFinalValue(const Aggplus::CMatrix* pPrevMatrix, TransformType oWithoutType) const { Aggplus::CMatrix oMatrix; @@ -862,6 +862,9 @@ namespace NSCSS for (const std::pair, TransformType>& oElement : m_oValue) { + if (oWithoutType == oElement.second) + continue; + switch(oElement.second) { case TransformMatrix: diff --git a/Common/3dParty/html/css/src/StyleProperties.h b/Common/3dParty/html/css/src/StyleProperties.h index a1bc1f0487..50564df18d 100644 --- a/Common/3dParty/html/css/src/StyleProperties.h +++ b/Common/3dParty/html/css/src/StyleProperties.h @@ -189,7 +189,8 @@ namespace NSCSS TransformMatrix, TransformTranslate, TransformScale, - TransformRotate + TransformRotate, + TransformNone } TransformType; typedef std::vector, TransformType>> MatrixValues; @@ -213,7 +214,7 @@ namespace NSCSS double ToDouble() const override; std::wstring ToWString() const override; - Aggplus::CMatrix GetFinalValue(const Aggplus::CMatrix* pPrevMatrix = NULL) const; + Aggplus::CMatrix GetFinalValue(const Aggplus::CMatrix* pPrevMatrix = NULL, TransformType oWithoutType = TransformNone) const; bool operator==(const CMatrix& oMatrix) const; }; diff --git a/DesktopEditor/raster/Metafile/svg/SvgObjects/CPath.cpp b/DesktopEditor/raster/Metafile/svg/SvgObjects/CPath.cpp index 5eef2b47ab..79b17081c7 100644 --- a/DesktopEditor/raster/Metafile/svg/SvgObjects/CPath.cpp +++ b/DesktopEditor/raster/Metafile/svg/SvgObjects/CPath.cpp @@ -8,7 +8,7 @@ namespace SVG #define EPSILON 0.05 #define CURVESTEP 0.05 #define MINCURVESTEP 0.001 - #define ARCSTEP 0.1 + #define ARCSTEP 0.5 #define MINARCSTEP 0.01 CPath::CPath(XmlUtils::CXmlNode& oNode, CSvgGraphicsObject* pParent) @@ -229,7 +229,10 @@ namespace SVG : m_pPath(pPath), m_oPosition{DBL_MIN, DBL_MIN}, m_oLastPoint{0, 0}, m_dAngle(0), m_pCurrentElement(NULL), m_unIndexElement(0), m_dCurveIndex(0), m_dCurveStep(CURVESTEP), m_dStartAngle(0), m_dEndAngle(0), m_dArcStep(ARCSTEP) { if (NULL != m_pPath) + { m_pCurrentElement = (*m_pPath)[m_unIndexElement++]; + m_oPosition = (*m_pCurrentElement)[0]; + } } bool CMovingPath::Move(double dX) @@ -237,7 +240,7 @@ namespace SVG if (NULL == m_pCurrentElement) return false; - while (dX != 0) + while (true) { switch (m_pCurrentElement->GetType()) { @@ -253,6 +256,7 @@ namespace SVG case EPathElement::HLine: { Point oPoint{(*m_pCurrentElement)[0]}; + m_dSkeepAngle = 0.; double dDx = oPoint.dX - m_oPosition.dX; double dDy = oPoint.dY - m_oPosition.dY; @@ -260,16 +264,17 @@ namespace SVG double dLineLength = std::sqrt(std::pow(dDx, 2) + std::pow(dDy, 2)); m_dAngle = std::atan2(dDy, dDx); - if (dLineLength > dX) + if (dLineLength >= dX) { m_oLastPoint = m_oPosition; m_oPosition += {std::cos(m_dAngle) * dX, std::sin(m_dAngle) * dX}; + m_dAngle *= 180. / M_PI; return true; } else { m_pCurrentElement = (*m_pPath)[m_unIndexElement++]; - m_oPosition = oPoint; + m_oPosition = m_oLastPoint = oPoint; return Move(dX - dLineLength); } } @@ -279,16 +284,26 @@ namespace SVG case EPathElement::TBezier: { Point oCurvePoint{0., 0.}; + m_dSkeepAngle = 0.; double dPrevValue = dX; while(std::abs(dX) > EPSILON && m_dCurveIndex <= 1 ) { if (MINCURVESTEP > m_dCurveStep) + { + m_dCurveStep = CURVESTEP; return true; + } - if (((0. < dPrevValue && 0. > dX) || (0. > dPrevValue && 0. < dX))) + if ((0. < dPrevValue && 0. > dX) || (0. > dPrevValue && 0. < dX)) + { + if (0. > dPrevValue && 0. < dX) + m_dSkeepAngle = 0; + else + m_dSkeepAngle = 180; m_dCurveStep /= 2.; + } if (dX > 0.) m_dCurveIndex += m_dCurveStep; @@ -305,12 +320,14 @@ namespace SVG UpdatePosition(oCurvePoint, dX); } - m_dCurveStep = CURVESTEP; + m_dCurveStep = CURVESTEP; return NextMove(dX, (*m_pCurrentElement)[1]); } case EPathElement::Arc: { + m_dSkeepAngle = 0.; + CArcElement *pArcElement = (CArcElement*)m_pCurrentElement; if (0. == m_dStartAngle && m_dStartAngle == m_dEndAngle) { @@ -319,11 +336,11 @@ namespace SVG m_dStartAngle = pArcElement->GetAngle ( Center.dX, Center.dY, (*pArcElement)[0].dX, (*pArcElement)[0].dY); m_dEndAngle = pArcElement->GetAngle ( Center.dX, Center.dY, (*pArcElement)[1].dX, (*pArcElement)[1].dY); - if (m_dStartAngle > m_dEndAngle) - std::swap(m_dStartAngle, m_dEndAngle); - m_oPosition = Center + pArcElement->GetPoint(m_dStartAngle); + if (m_dStartAngle > m_dEndAngle) + m_dArcStep *= -1; + m_oLastPoint = Center; } @@ -331,10 +348,13 @@ namespace SVG // поэтому необходимо разобраться в этом double dPrevValue = dX; - while(std::abs(dX) > EPSILON && m_dStartAngle < m_dEndAngle) + while(std::abs(m_dEndAngle - m_dStartAngle) > ARCSTEP) { - if (MINARCSTEP > m_dArcStep) + if (MINARCSTEP > std::abs(m_dArcStep)) + { + m_dArcStep = ARCSTEP * ((m_dStartAngle > m_dEndAngle) ? -1 : 1); return true; + } if (((0. < dPrevValue && 0. > dX) || (0. > dPrevValue && 0. < dX))) m_dArcStep /= 2.; @@ -368,7 +388,7 @@ namespace SVG double CMovingPath::GetAngle() const { - return m_dAngle; + return m_dAngle + m_dSkeepAngle; } void CMovingPath::UpdatePosition(const Point &oPoint, double &dX) @@ -377,7 +397,7 @@ namespace SVG double dDy = oPoint.dY - m_oPosition.dY; dX -= std::sqrt(std::pow(dDx, 2) + std::pow(dDy, 2)) * ((dX < 0) ? (-1) : 1); - m_dAngle = std::atan2(dDy, dDx); + m_dAngle = std::atan2(dDy, dDx) * 180. / M_PI; m_oPosition = oPoint; } diff --git a/DesktopEditor/raster/Metafile/svg/SvgObjects/CPath.h b/DesktopEditor/raster/Metafile/svg/SvgObjects/CPath.h index 7d41788fec..9109977d0a 100644 --- a/DesktopEditor/raster/Metafile/svg/SvgObjects/CPath.h +++ b/DesktopEditor/raster/Metafile/svg/SvgObjects/CPath.h @@ -688,6 +688,7 @@ namespace SVG IPathElement *m_pCurrentElement; unsigned int m_unIndexElement; + double m_dSkeepAngle; // Необходимо при рабое с кривыми Безье double m_dCurveIndex; double m_dCurveStep; diff --git a/DesktopEditor/raster/Metafile/svg/SvgObjects/CText.cpp b/DesktopEditor/raster/Metafile/svg/SvgObjects/CText.cpp index 50b1798182..e57b1b39c0 100644 --- a/DesktopEditor/raster/Metafile/svg/SvgObjects/CText.cpp +++ b/DesktopEditor/raster/Metafile/svg/SvgObjects/CText.cpp @@ -25,6 +25,9 @@ namespace SVG m_oFont.UpdateSize(16); m_wsText = StrUtils::TrimExtraEnding(oNode.GetText()); + if (!oNode.GetText().empty() && m_wsText.empty()) + m_wsText = oNode.GetText(); + if (!m_oX.SetValue(oNode.GetAttribute(L"x")) && NULL != dynamic_cast(m_pParent)) { TBounds oBounds = pParent->GetBounds(); @@ -48,7 +51,7 @@ namespace SVG return new CTSpan(oNode, pTSpanParent, pFontManager); } - CTSpan *CTSpan::Create(const std::wstring &wsValue, Point oPosition, CSvgGraphicsObject *pParent, NSFonts::IFontManager *pFontManager) + CTSpan *CTSpan::Create(const std::wstring &wsValue, const Point& oPosition, CSvgGraphicsObject *pParent, NSFonts::IFontManager *pFontManager) { CTSpan *pTSpanParent = dynamic_cast(pParent); @@ -167,6 +170,8 @@ namespace SVG return; ApplyTransform(pRenderer, oOldMatrix); + ApplyFill(pRenderer, pDefs, nTypePath, true); + ApplyStroke(pRenderer, nTypePath, true); } void CTSpan::ApplyFont(IRenderer* pRenderer, double& dX, double& dY) const @@ -297,12 +302,15 @@ namespace SVG double CTSpan::GetWidth() const { + if (m_wsText.empty()) + return 0.; + std::wstring wsName = (!m_oFont.GetFamily().Empty()) ? m_oFont.GetFamily().ToWString() : DefaultFontFamily; double dSize = m_oFont.GetSize().ToDouble(NSCSS::Pixel) * 72. / 25.4; - m_pFontManager->LoadFontByName(wsName, dSize, 0, 72, 72); + m_pFontManager->LoadFontByName(wsName, dSize, 0., 72., 72.); - m_pFontManager->LoadString1(m_wsText, 0, 0); + m_pFontManager->LoadString1(m_wsText, 0., 0.); TBBox oBox = m_pFontManager->MeasureString2(); double dWidth = 25.4 / 72.0 * (oBox.fMaxX - oBox.fMinX); @@ -314,14 +322,12 @@ namespace SVG if (NULL == pRenderer) return; - double dM11, dM12, dM21, dM22, dDx, dDy; - - pRenderer->GetTransform(&dM11, &dM12, &dM21, &dM22, &dDx, &dDy); + Aggplus::CMatrix oCurrentMatrix(m_oTransform.GetMatrix().GetFinalValue(NULL, NSCSS::NSProperties::TransformRotate)); double dXScale = 1., dYScale = 1.; - double dModuleM11 = std::abs(dM11); - double dModuleM22 = std::abs(dM22); + double dModuleM11 = std::abs(oCurrentMatrix.sx()); + double dModuleM22 = std::abs(oCurrentMatrix.sy()); if (!ISZERO(dModuleM11) && (dModuleM11 < 0.05 || dModuleM11 > 100)) dXScale /= dModuleM11; @@ -329,10 +335,17 @@ namespace SVG if (!ISZERO(dModuleM22) && (dModuleM22 < 0.05 || dModuleM22 > 100)) dYScale /= dModuleM22; + if (1. == dXScale && 1. == dYScale) + return; + dX /= dXScale; dY /= dYScale; dFontHeight /= dYScale; + double dM11, dM12, dM21, dM22, dDx, dDy; + + pRenderer->GetTransform(&dM11, &dM12, &dM21, &dM22, &dDx, &dDy); + Aggplus::CMatrix oMatrix(dM11, dM12, dM21, dM22, dDx, dDy); oMatrix.Scale(dXScale, dYScale); @@ -340,6 +353,12 @@ namespace SVG pRenderer->SetTransform(oMatrix.sx(), oMatrix.shy(), oMatrix.shx(), oMatrix.sy(), oMatrix.tx(), oMatrix.ty()); } + void CTSpan::SetPosition(const Point &oPosition) + { + m_oX = oPosition.dX; + m_oY = oPosition.dY; + } + CText::CText(XmlUtils::CXmlNode &oNode, CSvgGraphicsObject *pParent, NSFonts::IFontManager *pFontManager) : CTSpan(oNode, NULL, pFontManager) { @@ -402,8 +421,6 @@ namespace SVG m_oX = oFirstPoint.dX; m_oY = oFirstPoint.dY; } - - DevideByTSpan(m_wsText); } } } @@ -413,10 +430,54 @@ namespace SVG if (NULL == pRenderer || bIsClip || NULL == m_pPath) return false; - for (const CSvgGraphicsObject* pTSpan : m_arObjects) + TBounds oBounds{0., 0., 0., 0.}; + + double dX = m_oX.ToDouble(NSCSS::Pixel, oBounds.m_dRight - oBounds.m_dLeft); + double dY = m_oY.ToDouble(NSCSS::Pixel, oBounds.m_dBottom - oBounds.m_dTop); + + int nPathType = 0; + Aggplus::CMatrix oOldMatrix(1., 0., 0., 1., 0, 0); + ApplyStyle(pRenderer, pDefs, nPathType, oOldMatrix); + + ApplyFont(pRenderer, dX, dY); + + CMovingPath oMovingPath(m_pPath); + + for (const wchar_t& wsSymbol : m_wsText) + { + CTSpan *pTSpan = CTSpan::Create(std::wstring(1, wsSymbol), {0., 0.}, (CText*)this, m_pFontManager); + + if (NULL == pTSpan) + continue; + + pTSpan->InheritStyles(this); + + double dWidthSpan = pTSpan->GetWidth(); + + if (!oMovingPath.Move(dWidthSpan / 2)) + { + delete pTSpan; + break; + } + + double dAngle = oMovingPath.GetAngle(); + Point oPoint = oMovingPath.GetPosition() - Point{(dWidthSpan / 2.) * std::cos(dAngle / 180 * M_PI), (dWidthSpan / 2.) * std::sin(dAngle / 180 * M_PI)}; + + pTSpan->SetPosition(oPoint); + pTSpan->SetTransform({std::make_pair(L"transform", L"rotate(" + std::to_wstring(dAngle) + L',' + std::to_wstring(oPoint.dX) + L',' + std::to_wstring(oPoint.dY) + L')')}, 0, true); + pTSpan->Draw(pRenderer, pDefs, bIsClip); + delete pTSpan; + + if (!oMovingPath.Move(dWidthSpan / 2)) + break; + } + + pRenderer->SetTransform(oOldMatrix.sx(), oOldMatrix.shy(), oOldMatrix.shx(), oOldMatrix.sy(), oOldMatrix.tx(), oOldMatrix.ty()); + return true; + } CTextPath* CTextPath::Create(XmlUtils::CXmlNode &oNode, CSvgGraphicsObject *pParent, NSFonts::IFontManager *pFontManager, const CSvgFile* pFile) @@ -426,19 +487,4 @@ namespace SVG return new CTextPath(oNode, pParent, pFontManager, pFile); } - - void CTextPath::DevideByTSpan(const std::wstring& wsText) - { - TBounds oBounds = GetBounds(); - - double dXStep = (oBounds.m_dRight - oBounds.m_dLeft) / wsText.length(); - Point oPosition{m_oX.ToDouble(NSCSS::Pixel), m_oY.ToDouble(NSCSS::Pixel)}; - - for (const wchar_t& oWChar : wsText) - { - AddObject(CTSpan::Create(std::wstring(1, oWChar), oPosition, this, m_pFontManager)); - oPosition.dX += dXStep; - } - } - } diff --git a/DesktopEditor/raster/Metafile/svg/SvgObjects/CText.h b/DesktopEditor/raster/Metafile/svg/SvgObjects/CText.h index 3c185a0e9a..03d94e1ec7 100644 --- a/DesktopEditor/raster/Metafile/svg/SvgObjects/CText.h +++ b/DesktopEditor/raster/Metafile/svg/SvgObjects/CText.h @@ -16,7 +16,7 @@ namespace SVG virtual ~CTSpan(); static CTSpan* Create(XmlUtils::CXmlNode& oNode, CSvgGraphicsObject* pParent = NULL, NSFonts::IFontManager* pFontManager = NULL); - static CTSpan* Create(const std::wstring& wsValue, Point oPosition, CSvgGraphicsObject* pParent = NULL, NSFonts::IFontManager* pFontManager = NULL); + static CTSpan* Create(const std::wstring& wsValue, const Point& oPosition, CSvgGraphicsObject* pParent = NULL, NSFonts::IFontManager* pFontManager = NULL); void SetData(const std::map& mAttributes, unsigned short ushLevel, bool bHardMode = false) override; @@ -29,6 +29,7 @@ namespace SVG CTSpan* Copy() const override; private: void ApplyStyle(IRenderer* pRenderer, const CDefs *pDefs, int& nTypePath, Aggplus::CMatrix& oOldMatrix) const override; + void ApplyFont(IRenderer* pRenderer, double& dX, double& dY) const; TBounds GetBounds() const override; @@ -36,6 +37,7 @@ namespace SVG double GetWidth() const; void Normalize(IRenderer* pRenderer, double& dX, double& dY, double& dFontHeight) const; + void SetPosition(const Point& oPosition); NSFonts::IFontManager* m_pFontManager; @@ -72,8 +74,6 @@ namespace SVG static CTextPath* Create(XmlUtils::CXmlNode& oNode, CSvgGraphicsObject* pParent = NULL, NSFonts::IFontManager* pFontManager = NULL, const CSvgFile* pFile = NULL); private: - void DevideByTSpan(const std::wstring& wsText); - const CPath *m_pPath; }; }