Files
Yajbir Singh f1b860b25c
Some checks failed
check / markdownlint (push) Has been cancelled
check / spellchecker (push) Has been cancelled
updated
2025-12-11 19:03:17 +05:30

1748 lines
46 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* (c) Copyright Ascensio System SIA 2010-2023
*
* This program is a free software product. You can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License (AGPL)
* version 3 as published by the Free Software Foundation. In accordance with
* Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect
* that Ascensio System SIA expressly excludes the warranty of non-infringement
* of any third-party rights.
*
* This program is distributed WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For
* details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
*
* You can contact Ascensio System SIA at 20A-6 Ernesta Birznieka-Upish
* street, Riga, Latvia, EU, LV-1050.
*
* The interactive user interfaces in modified source and object code versions
* of the Program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU AGPL version 3.
*
* Pursuant to Section 7(b) of the License you must retain the original Product
* logo when distributing the program. Pursuant to Section 7(e) we decline to
* grant you any rights under trademark law for use of our trademarks.
*
* All the Product's GUI elements, including illustrations and icon sets, as
* well as technical writing content are licensed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International. See the License
* terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
*
*/
#include "Utils.h"
#include "../../DesktopEditor/common/StringExt.h"
#include "../../DesktopEditor/common/Types.h"
#include "../../DesktopEditor/xml/include/xmlutils.h"
#include "../../DesktopEditor/graphics/IRenderer.h"
#include <math.h>
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
#ifndef xpsUnitToMM
#define xpsUnitToMM(x) ((x) * 25.4 / 96)
#endif
#define IsNumber(X) \
('0' == (X)\
|| '1' == (X)\
|| '2' == (X)\
|| '3' == (X)\
|| '4' == (X)\
|| '5' == (X)\
|| '6' == (X)\
|| '7' == (X)\
|| '8' == (X)\
|| '9' == (X)\
|| '-' == (X)\
|| '.' == (X)\
|| 'e' == (X)\
|| 'E' == (X))
#define GetChar(STRING, POS) STRING[POS++]
#define LookChar(STRING, POS) STRING[POS]
#ifndef SQR
#define SQR(X) ((X)*(X))
#endif
#ifndef SQRT
#define SQRT(X) (sqrt(X))
#endif
namespace XPSEllipse
{
double AngToEllPrm (double fAngle, double fXRad, double fYRad)
{
// Функция для перевода реального угла в параметрическое задание эллписа
// т.е. x= a cos(t) y = b sin(t) - параметрическое задание эллписа.
// x = r cos(p), y = r sin(p) => t = atan2( sin(p) / b, cos(p) / a );
return atan2(sin(fAngle) / fYRad, cos(fAngle) / fXRad);
}
void CurveTo (IRenderer* pRenderer, Aggplus::CMatrix& oTransform, double dCX1, double dCY1, double dCX2, double dCY2, double dX2, double dY2)
{
oTransform.TransformPoint(dCX1, dCY1);
oTransform.TransformPoint(dCX2, dCY2);
oTransform.TransformPoint(dX2, dY2);
pRenderer->PathCommandCurveTo(xpsUnitToMM(dCX1), xpsUnitToMM(dCY1), xpsUnitToMM(dCX2), xpsUnitToMM(dCY2), xpsUnitToMM(dX2), xpsUnitToMM(dY2));
}
void LineTo (IRenderer* pRenderer, Aggplus::CMatrix& oTransform, double dX, double dY)
{
oTransform.TransformPoint(dX, dY);
pRenderer->PathCommandLineTo(xpsUnitToMM(dX), xpsUnitToMM(dY));
}
void MoveTo (IRenderer* pRenderer, Aggplus::CMatrix& oTransform, double dX, double dY)
{
oTransform.TransformPoint(dX, dY);
pRenderer->PathCommandMoveTo(xpsUnitToMM(dX), xpsUnitToMM(dY));
}
void EllipseArc3 (IRenderer* pRenderer, Aggplus::CMatrix& oTransform, double fX, double fY, double fXRad, double fYRad, double dAngle1, double dAngle2, double *pfXCur, double *pfYCur, bool bClockDirection)
{
// Рассчитаем начальную, конечную и контрольные точки
double fX1 = 0.0, fX2 = 0.0, fY1 = 0.0, fY2 = 0.0;
double fCX1 = 0.0, fCX2 = 0.0, fCY1 = 0.0, fCY2 = 0.0;
double fAlpha = sin(dAngle2 - dAngle1) * (sqrt(4.0 + 3.0 * tan((dAngle2 - dAngle1) / 2.0) * tan((dAngle2 - dAngle1) / 2.0)) - 1.0) / 3.0;
double fKoef = 1;
fX1 = fX + fXRad * cos(dAngle1);
fY1 = fY + fYRad * sin(dAngle1);
fX2 = fX + fXRad * cos(dAngle2);
fY2 = fY + fYRad * sin(dAngle2);
fCX1 = fX1 - fAlpha * fXRad * sin(dAngle1);
fCY1 = fY1 + fAlpha * fYRad * cos(dAngle1);
fCX2 = fX2 + fAlpha * fXRad * sin(dAngle2);
fCY2 = fY2 - fAlpha * fYRad * cos(dAngle2);
if (!bClockDirection)
{
CurveTo(pRenderer, oTransform, fCX1, fCY1, fCX2, fCY2, fX2, fY2);
*pfXCur = fX2;
*pfYCur = fY2;
}
else
{
CurveTo(pRenderer, oTransform, fCX2, fCY2, fCX1, fCY1, fX1, fY1);
*pfXCur = fX1;
*pfYCur = fY1;
}
}
void EllipseArc2 (IRenderer* pRenderer, Aggplus::CMatrix& oTransform, double fX, double fY, double fXRad, double fYRad, double fAngle1, double fAngle2, bool bClockDirection)
{
// переведем углы в радианы
double dAngle1 = fAngle1 * 3.141592 / 180;
double dAngle2 = fAngle2 * 3.141592 / 180;
// Выясним в каких четвертях находятся начальная и конечная точки
unsigned int nFirstPointQuard = int(fAngle1) / 90 + 1;
unsigned int nSecondPointQuard = int(fAngle2) / 90 + 1;
nSecondPointQuard = std::min((unsigned int)4, std::max((unsigned int)1, nSecondPointQuard));
nFirstPointQuard = std::min((unsigned int)4, std::max((unsigned int)1, nFirstPointQuard));
// Проведем линию в начальную точку дуги
double fStartX = 0.0, fStartY = 0.0, fEndX = 0.0, fEndY = 0.0;
fStartX = fX + fXRad * cos(AngToEllPrm(dAngle1, fXRad, fYRad));
fStartY = fY + fYRad * sin(AngToEllPrm(dAngle1, fXRad, fYRad));
LineTo(pRenderer, oTransform, fStartX, fStartY);
// Дальше рисуем по четверям
double fCurX = fStartX, fCurY = fStartY;
double dStartAngle = dAngle1;
double dEndAngle = 0;
if (!bClockDirection)
{
for (unsigned int nIndex = nFirstPointQuard; nIndex <= nSecondPointQuard; nIndex++)
{
if (nIndex == nSecondPointQuard)
dEndAngle = dAngle2;
else
dEndAngle = (90 * (nIndex)) * 3.141592f / 180;
if (!(nIndex == nFirstPointQuard))
dStartAngle = (90 * (nIndex - 1)) * 3.141592f / 180;
EllipseArc3(pRenderer, oTransform, fX, fY, fXRad, fYRad, AngToEllPrm(dStartAngle, fXRad, fYRad), AngToEllPrm(dEndAngle, fXRad, fYRad), &fEndX, &fEndY, false);
}
}
else
{
for (unsigned int nIndex = nFirstPointQuard; nIndex >= nSecondPointQuard; nIndex--)
{
if (nIndex == nFirstPointQuard)
dStartAngle = dAngle1;
else
dStartAngle = (90 * (nIndex)) * 3.141592f / 180;
if (!(nIndex == nSecondPointQuard))
dEndAngle = (90 * (nIndex - 1)) * 3.141592f / 180;
else
dEndAngle = dAngle2;
EllipseArc3(pRenderer, oTransform, fX, fY, fXRad, fYRad, AngToEllPrm(dStartAngle, fXRad, fYRad), AngToEllPrm(dEndAngle, fXRad, fYRad), &fEndX, &fEndY, false);
}
}
}
void EllipseArc (IRenderer* pRenderer, Aggplus::CMatrix& oTransform, double fX, double fY, double fXRad, double fYRad, double fAngle1, double fAngle2, bool bClockDirection)
{
while (fAngle1 < 0)
fAngle1 += 360;
while (fAngle1 > 360)
fAngle1 -= 360;
while (fAngle2 < 0)
fAngle2 += 360;
while (fAngle2 >= 360)
fAngle2 -= 360;
if (!bClockDirection)
{
if (fAngle1 <= fAngle2)
EllipseArc2(pRenderer, oTransform, fX, fY, fXRad, fYRad, fAngle1, fAngle2, false);
else
{
EllipseArc2(pRenderer, oTransform, fX, fY, fXRad, fYRad, fAngle1, 360, false);
EllipseArc2(pRenderer, oTransform, fX, fY, fXRad, fYRad, 0, fAngle2, false);
}
}
else
{
if (fAngle1 >= fAngle2)
EllipseArc2(pRenderer, oTransform, fX, fY, fXRad, fYRad, fAngle1, fAngle2, true);
else
{
EllipseArc2(pRenderer, oTransform, fX, fY, fXRad, fYRad, fAngle1, 0, true);
EllipseArc2(pRenderer, oTransform, fX, fY, fXRad, fYRad, 360, fAngle2, true);
}
}
}
void Ellipse (IRenderer* pRenderer, Aggplus::CMatrix& oTransform, double fX, double fY, double fXRad, double fYRad)
{
MoveTo(pRenderer, oTransform, fX - fXRad, fY);
double c_fKappa = 0.552;
CurveTo(pRenderer, oTransform, fX - fXRad, fY + fYRad * c_fKappa, fX - fXRad * c_fKappa, fY + fYRad, fX, fY + fYRad);
CurveTo(pRenderer, oTransform, fX + fXRad * c_fKappa, fY + fYRad, fX + fXRad, fY + fYRad * c_fKappa, fX + fXRad, fY);
CurveTo(pRenderer, oTransform, fX + fXRad, fY - fYRad * c_fKappa, fX + fXRad * c_fKappa, fY - fYRad, fX, fY - fYRad);
CurveTo(pRenderer, oTransform, fX - fXRad * c_fKappa, fY - fYRad, fX - fXRad, fY - fYRad * c_fKappa, fX - fXRad, fY);
}
void AppendEllipseArc(IRenderer* pRenderer, Aggplus::CMatrix& oTransform, double fX, double fY, double fXRad, double fYRad, double fStartAngle, double fEndAngle, bool bClockDirection)
{
if (fXRad <= 0 || fYRad <= 0)
return;
if (fabs(fEndAngle - fStartAngle) >= 360) // Целый эллипс
{
Ellipse(pRenderer, oTransform, fX, fY, fXRad, fYRad);
}
else // Дуга эллипса
{
EllipseArc(pRenderer, oTransform, fX, fY, fXRad, fYRad, fStartAngle, fEndAngle, bClockDirection);
}
}
inline void GetEllipseKoefs (const double& dX1, const double& dY1, const double& dRadX, const double& dRadY, double& dA1, double& dB1, double& dA2, double& dB2, double& dC2, double& dRoot)
{
// Y = A1 * X + B1
dA1 = -dX1 / dY1 * SQR(dRadY) / SQR(dRadX);
dB1 = (SQR(dX1) / SQR(dRadX) + SQR(dY1) / SQR(dRadY)) * SQR(dRadY) / (2 * dY1);
// Получаем квадратное уравнение A2 * X^2 + B2 * X + C2 = 0 (причем A2 != 0 в нашей ситуации)
dA2 = SQR(dA1) / SQR(dRadY) + 1 / SQR(dRadX);
dB2 = 2 * dA1 * dB1 / SQR(dRadY);
dC2 = SQR(dB1) / SQR(dRadY) - 1;
dRoot = (SQR(dB2) - 4 * dA2 * dC2);
}
inline bool GetEllipsesCommonCase (const double& dX1, const double& dY1, double& dRadX, double& dRadY, double& dCx1, double& dCy1, double& dCx2, double& dCy2)
{
double dA1, dB1, dA2, dB2, dC2, dRoot;
GetEllipseKoefs(dX1, dY1, dRadX, dRadY, dA1, dB1, dA2, dB2, dC2, dRoot);
if (dRoot < 0)
{
double dK1 = (SQR(dA1) + SQR(dRadY) / SQR(dRadX)); // dK1 всегда положительное (поэтому спокойно на него делим)
double dK2 = 2 * dA1 * dB1;
double dK3 = SQR(dB1);
double dK4 = dK3 - SQR(dK2) / (4 * dK1);
if (dK4 < 0)
return false; // Такого быть не должно
double dNewRadY = SQRT(dK4);
double dKoef = dNewRadY / dRadY;
if (dKoef > 1)
{
dRadX *= dKoef;
dRadY *= dKoef;
GetEllipseKoefs(dX1, dY1, dRadX, dRadY, dA1, dB1, dA2, dB2, dC2, dRoot);
}
}
if (dRoot < -0.001)
{
// Такого быть не должно
return false;
}
else if (dRoot < 0)
{
dCx1 = -dB2 / (2 * dA2);
dCx2 = dCx1;
}
else
{
dCx1 = (-dB2 + SQRT(dRoot)) / (2 * dA2);
dCx2 = (-dB2 - SQRT(dRoot)) / (2 * dA2);
}
dCy1 = dA1 * dCx1 + dB1;
dCy2 = dA1 * dCx2 + dB1;
return true;
}
inline bool GetEllipsesVerticalCase(const double& dX1, double& dRadX, double& dRadY, double& dCx1, double& dCy1, double& dCx2, double& dCy2)
{
if (abs(dX1) < 0.001)
return false;
// Центры искомых эллипсов лежат на вертикальной прямой
dCx1 = dX1 / 2;
dCx2 = dCx1;
double dRoot = SQR(dRadY) - SQR(dRadY) / SQR(dRadX) * SQR(dCx1);
if (dRoot < 0)
{
double dNewRadX = dX1 / 2;
double dKoef = dNewRadX / dRadX;
if (dKoef > 1)
{
dRadX *= dKoef;
dRadY *= dKoef;
dRoot = SQR(dRadY) - SQR(dRadY) / SQR(dRadX) * SQR(dCx1);
}
}
if (dRoot < -0.001)
{
// Такого быть не должно
return false;
}
else if (dRoot < 0)
{
dCy1 = 0;
dCy2 = 0;
}
else
{
dCy1 = SQRT(dRoot);
dCy2 = -dCy1;
}
return true;
}
inline bool GetEllipses (const double& dX1, const double& dY1, double& dRadX, double& dRadY, double& dCx1, double& dCy1, double& dCx2, double& dCy2)
{
if (abs(dY1) > 0.001)
return GetEllipsesCommonCase(dX1, dY1, dRadX, dRadY, dCx1, dCy1, dCx2, dCy2);
else
return GetEllipsesVerticalCase(dX1, dRadX, dRadY, dCx1, dCy1, dCx2, dCy2);
}
}
namespace XPS
{
static inline double GetEllipseAngle(const double& dCx, const double& dCy, const double& dRadX, const double dRadY, const double& dX, const double& dY)
{
// Определим квадрант
int nQuarter = -1;
if (dX >= dCx)
{
if (dY <= dCy)
nQuarter = 3;
else
nQuarter = 0;
}
else
{
if (dY <= dCy)
nQuarter = 2;
else
nQuarter = 1;
}
double dDist = SQRT(SQR(dX - dCx) + SQR(dY - dCy));
double dRadAngle = asin(abs(dY - dCy) / dDist);
double dAngle = dRadAngle * 180 / 3.14159265358979323846;
switch (nQuarter)
{
case 1: dAngle = 180 - dAngle; break;
case 2: dAngle = 180 + dAngle; break;
case 3: dAngle = 360 - dAngle; break;
}
return dAngle;
}
static inline bool SkipWhiteSpaces(const wchar_t* wsString, int& nPos, const int& nLen)
{
while (1)
{
if (nPos >= nLen)
return false;
wchar_t wChar = LookChar(wsString, nPos);
if (' ' == wChar)
{
nPos++;
continue;
}
else
break;
}
return true;
}
static inline bool SkipWhiteSpacesAndCommas(const wchar_t* wsString, int& nPos, const int& nLen)
{
while (1)
{
if (nPos >= nLen)
return false;
wchar_t wChar = LookChar(wsString, nPos);
if (' ' == wChar || ',' == wChar)
{
nPos++;
continue;
}
else
break;
}
return true;
}
static inline double GetDouble(const wchar_t* wsString, int& nPos, const int& nLen)
{
if (!SkipWhiteSpacesAndCommas(wsString, nPos, nLen))
return 0.0;
wchar_t wChar = GetChar(wsString, nPos);
if (IsNumber(wChar))
{
bool bNegative = false;
int nInt = 0;
double dFloat = 0.0, dScale = 0.1;
if ('-' == wChar)
{
bNegative = true;
}
else if ('.' == wChar)
{
goto doReal;
}
else
{
nInt = wChar - '0';
}
while (1)
{
wChar = LookChar(wsString, nPos);
if (isdigit(wChar))
{
nPos++;
nInt = nInt * 10 + (wChar - '0');
}
else if ('.' == wChar)
{
nPos++;
dFloat = (double)nInt;
goto doReal;
}
else if ('e' == wChar || 'E' == wChar)
{
nPos++;
dFloat = (double)nInt;
goto doExponent;
}
else
{
break;
}
}
return (bNegative ? (double)(-nInt) : (double)nInt);
doReal:
while (1)
{
wChar = LookChar(wsString, nPos);
if ('e' == wChar || 'E' == wChar)
{
nPos++;
goto doExponent;
}
else if (!isdigit(wChar))
break;
nPos++;
dFloat = dFloat + dScale * (wChar - '0');
dScale *= 0.1;
}
return (bNegative ? (double)(-dFloat) : (double)dFloat);
doExponent:
wChar = GetChar(wsString, nPos);
bool bNegativeExponent = false;
int nExp = 0;
if ('-' == wChar)
{
bNegativeExponent = true;
}
else if ('+' == wChar)
{
bNegativeExponent = false;
}
else
{
nExp = wChar - '0';
}
while (1)
{
wChar = LookChar(wsString, nPos);
if (!isdigit(wChar))
break;
nPos++;
nExp = nExp * 10 + (wChar - '0');
}
dFloat = (bNegative ? (double)(-dFloat) : (double)dFloat);
nExp = std::max(20, std::min(0, nExp));
while (nExp)
{
if (bNegativeExponent)
dFloat /= 10;
else
dFloat *= 10;
nExp--;
}
return dFloat;
}
return 0.0;
}
static inline int GetInt (const wchar_t* wsString, int& nPos, const int& nLen)
{
if (!SkipWhiteSpaces(wsString, nPos, nLen))
return 0;
wchar_t wChar = GetChar(wsString, nPos);
if (IsNumber(wChar))
{
bool bNegative = false;
int nInt = 0;
if ('-' == wChar)
{
bNegative = true;
}
else
{
nInt = wChar - '0';
}
while (1)
{
wChar = LookChar(wsString, nPos);
if (isdigit(wChar))
{
nPos++;
nInt = nInt * 10 + (wChar - '0');
}
else
{
break;
}
}
return (bNegative ? -nInt : nInt);
}
return 0;
}
static inline bool GetBool(const wchar_t* wsString, int& nPos, const int& nLen)
{
if (!SkipWhiteSpacesAndCommas(wsString, nPos, nLen))
return false;
int nBufPos = 0;
wchar_t pBuffer[32];
memset(pBuffer, 0x00, sizeof(wchar_t) * 32);
wchar_t wChar = LookChar(wsString, nPos);
while (' ' != wChar)
{
pBuffer[nBufPos++] = wChar;
nPos++;
if (nPos >= nLen)
break;
wChar = LookChar(wsString, nPos);
}
CWString wsBool(pBuffer, false, nBufPos);
if ((wsBool == L"true") || (wsBool == L"t") || (wsBool == L"1") || (wsBool == L"on"))
return true;
return false;
}
static inline void AppendArcTo(const wchar_t* wsString, int& nPos, const int& nLen, double& dCurX, double& dCurY, IRenderer* pRenderer,bool bRelative)
{
double dRadX = GetDouble(wsString, nPos, nLen);
double dRadY = GetDouble(wsString, nPos, nLen);
double dAngle = GetDouble(wsString, nPos, nLen);
bool bIsLarge = !GetBool(wsString, nPos, nLen);
bool bClockwise = !GetBool(wsString, nPos, nLen);
double dX0 = dCurX;
double dY0 = dCurY;
double dX1 = GetDouble(wsString, nPos, nLen);
double dY1 = GetDouble(wsString, nPos, nLen);
if (bRelative)
{
dX1 += dCurX;
dY1 += dCurY;
}
double _dX1 = dX1;
double _dY1 = dY1;
if (abs(dRadX) < 0.001 || abs(dRadY) < 0.001)
{
pRenderer->PathCommandLineTo(_dX1, _dY1);
dCurX = dX1;
dCurY = dY1;
return;
}
// Точку X0, Y0 переносим в начало координат и поворачиваем на обратный угол, чтобы искомые эллипсы встали правильно
Aggplus::CMatrix oTransform, oInverse;
oTransform.Rotate(-dAngle);
oTransform.Translate(-dX0, -dY0);
oInverse.Translate(dX0, dY0);
oInverse.Rotate(dAngle);
double dTestX = 0, dTestY = 0;
oTransform.TransformPoint(dX1, dY1);
oInverse.TransformPoint(dTestX, dTestY);
double dCx1, dCy1, dCx2, dCy2;
// Ищем эллипсы, точками пересечения которых являются (0, 0) и (dX1, dY1)
if (!XPSEllipse::GetEllipses(dX1, dY1, dRadX, dRadY, dCx1, dCy1, dCx2, dCy2))
{
// Такого не должно быть
pRenderer->PathCommandLineTo(_dX1, _dY1);
dCurX = _dX1;
dCurY = _dY1;
return;
}
//double dTest1 = SQR(dCx1) / SQR(dRadX) + SQR(dCy1) / SQR(dRadY);
//double dTest2 = SQR(dCx1 - dX1) / SQR(dRadX) + SQR(dCy1 - dY1) / SQR(dRadY);
//double dTest3 = SQR(dCx2) / SQR(dRadX) + SQR(dCy2) / SQR(dRadY);
//double dTest4 = SQR(dCx2 - dX1) / SQR(dRadX) + SQR(dCy2 - dY1) / SQR(dRadY);
// Теперь у нас есть 2 эллипса, нужно определить дугу, которую нам и надо отрисовать.
// Для начала найдем углы начальной и конечной точек для обоих эллипсов.
double dAngleStart1 = GetEllipseAngle(dCx1, dCy1, dRadX, dRadY, 0, 0);
double dAngleEnd1 = GetEllipseAngle(dCx1, dCy1, dRadX, dRadY, dX1, dY1);
if (dAngleEnd1 < dAngleStart1)
dAngleEnd1 += 360;
double dAngleStart2 = GetEllipseAngle(dCx2, dCy2, dRadX, dRadY, 0, 0);
double dAngleEnd2 = GetEllipseAngle(dCx2, dCy2, dRadX, dRadY, dX1, dY1);
if (dAngleEnd2 < dAngleStart2)
dAngleEnd2 += 360;
double dCx, dCy, dAngleStart, dAngleEnd;
if ((dAngleEnd1 - dAngleStart1 > 180 && ((bIsLarge && !bClockwise) || (!bIsLarge && bClockwise)))
|| (dAngleEnd1 - dAngleStart1 <= 180 && ((bIsLarge && bClockwise) || (!bIsLarge && !bClockwise))))
{
dCx = dCx1;
dCy = dCy1;
dAngleStart = dAngleStart1;
dAngleEnd = dAngleEnd1;
}
else
{
dCx = dCx2;
dCy = dCy2;
dAngleStart = dAngleStart2;
dAngleEnd = dAngleEnd2;
}
// Аппроксимируем эллипс кривыми Безье
XPSEllipse::AppendEllipseArc(pRenderer, oInverse, dCx, dCy, dRadX, dRadY, dAngleStart, dAngleEnd, bClockwise);
dCurX = _dX1;
dCurY = _dY1;
}
}
namespace XPS
{
int GetDigit(wchar_t wChar)
{
if (wChar >= '0' && wChar <= '9')
return (int)(wChar - '0');
if (wChar >= 'a' && wChar <= 'f')
return 10 + (int)(wChar - 'a');
if (wChar >= 'A' && wChar <= 'F')
return 10 + (int)(wChar - 'A');
return 0;
}
bool IsAlpha(wchar_t wChar)
{
return (((wChar >= 'A') && (wChar <= 'Z')) || ((wChar >= 'a') && (wChar <= 'z')));
}
double GetDouble(const CWString& wsString)
{
std::wstring sTmp(wsString.c_str(), wsString.size());
return std::stod(sTmp);
}
double GetDouble(const std::wstring& wsString)
{
return std::stod(wsString.c_str());
}
int GetInteger(const std::wstring& wsString)
{
return std::stoi(wsString.c_str());
}
bool GetBool(const std::wstring& wsString)
{
std::wstring wsStr = wsString;
NSStringExt::ToLower(wsStr);
if ((wsStr == L"true") || (wsStr == L"t") || (wsStr == L"1") || (wsStr == L"on"))
return true;
return false;
}
void GetBgra(const std::wstring& wsString, int& nBgr, int& nAlpha)
{
if (L'#' == wsString[0])
{
std::wstring wsStr = wsString.substr(1);
while (wsStr.length() < 6)
wsStr = L"0" + wsStr;
while (wsStr.length() < 8)
wsStr = L"F" + wsStr;
nAlpha = GetDigit(wsStr[0]);
nAlpha = nAlpha << 4;
nAlpha += GetDigit(wsStr[1]);
nBgr = GetDigit(wsStr[6]);
nBgr = nBgr << 4;
nBgr += GetDigit(wsStr[7]);
nBgr = nBgr << 4;
nBgr += GetDigit(wsStr[4]);
nBgr = nBgr << 4;
nBgr += GetDigit(wsStr[5]);
nBgr = nBgr << 4;
nBgr += GetDigit(wsStr[2]);
nBgr = nBgr << 4;
nBgr += GetDigit(wsStr[3]);
}
else if (L's' == wsString[0] && L'c' == wsString[1] && L'#' == wsString[2])
{
std::wstring wsStr = wsString.substr(3);
std::vector<std::wstring> arrElements = NSStringExt::Split(wsStr, L',');
if (3 == arrElements.size())
{
nAlpha = 255;
nBgr = (((int)(std::min(GetDouble(arrElements[2]), 1.0) * 255)) << 16) + (((int)(std::min(GetDouble(arrElements[1]), 1.0) * 255)) << 8) + ((int)(std::min(GetDouble(arrElements[0]), 1.0) * 255));
}
else if (4 == arrElements.size())
{
nAlpha = GetDouble(arrElements[0]) * 255;
nBgr = (((int)(std::min(GetDouble(arrElements[3]), 1.0) * 255)) << 16) + (((int)(std::min(GetDouble(arrElements[2]), 1.0) * 255)) << 8) + ((int)(std::min(GetDouble(arrElements[1]), 1.0) * 255));
}
}
else
return;
}
unsigned char GetCapStyle(const std::string& wsCapStyle)
{
BYTE nCapStyle = Aggplus::LineCapFlat;
if (wsCapStyle == "Flat")
nCapStyle = Aggplus::LineCapFlat;
else if (wsCapStyle == "Round")
nCapStyle = Aggplus::LineCapRound;
else if (wsCapStyle == "Square")
nCapStyle = Aggplus::LineCapSquare;
else if (wsCapStyle == "Triangle")
nCapStyle = Aggplus::LineCapTriangle;
return nCapStyle;
}
std::wstring NormalizePath(const std::wstring& wsPath)
{
#ifdef WIN32
std::wstring wsResult = wsPath;
NSStringExt::Replace(wsResult, L"/", L"\\");
while (std::wstring::npos != wsResult.find(L"\\\\"))
{
NSStringExt::Replace(wsResult, L"\\\\", L"\\");
}
return wsResult;
#else
std::wstring wsResult = wsPath;
NSStringExt::Replace(wsResult, L"//", L"/");
NSStringExt::Replace(wsResult, L"//", L"/");
return wsResult;
#endif
}
std::wstring GetPath(const std::wstring& wsPath)
{
std::wstring wsResult;
wsResult = wsPath.substr(0, wsPath.find_last_of('/') + 1);
return wsResult;
}
std::wstring GetFileName(const std::wstring& wsPath)
{
int nCommaPos = wsPath.find_last_of(L'.');
int nSlashPos = wsPath.find_last_of(L'/');
if (std::wstring::npos == nCommaPos)
nCommaPos = wsPath.length();
if (std::wstring::npos == nSlashPos)
nSlashPos = -1;
if (nCommaPos < nSlashPos)
return L"";
std::wstring wsResult = wsPath.substr(nSlashPos + 1, nCommaPos - nSlashPos - 1);
return wsResult;
}
std::wstring GetFileExtension(const std::wstring& wsPath)
{
int nCommaPos = wsPath.find_last_of(L'.');
if (std::wstring::npos == nCommaPos)
return L"";
std::wstring wsResult = wsPath.substr(nCommaPos + 1);
return wsResult;
}
std::wstring RemoveNamespace(const std::wstring& wsString)
{
std::wstring wsResult;
int nPos = wsString.find(L":");
if (std::wstring::npos != nPos)
wsResult = wsString.substr(nPos + 1);
else
wsResult = wsString;
return wsResult;
}
std::vector<std::vector<std::wstring>> Split(const std::wstring& wsString, wchar_t wDelim1, wchar_t wDelim2)
{
std::vector<std::vector<std::wstring>> arrResult;
std::vector<std::wstring> arrStrings = NSStringExt::Split(wsString, wDelim1);
int nCount = arrStrings.size();
for (int nIndex = 0; nIndex < nCount; nIndex++)
{
std::vector<std::wstring> arrStr = NSStringExt::Split(arrStrings[nIndex], wDelim2);
arrResult.push_back(arrStr);
}
return arrResult;
}
void ReadAttribute(XmlUtils::CXmlLiteReader& oReader, const wchar_t* wsAttrName, std::wstring& wsAttr)
{
if (oReader.GetAttributesCount() <= 0)
return;
if (!oReader.MoveToFirstAttribute())
return;
std::wstring wsName = oReader.GetName();
while (!wsName.empty())
{
if (wsAttrName == wsName)
{
wsAttr = oReader.GetText();
break;
}
if (!oReader.MoveToNextAttribute())
break;
wsName = oReader.GetName();
}
oReader.MoveToElement();
}
void ReadAttribute(XmlUtils::CXmlLiteReader& oReader, const wchar_t* wsAttrName, CWString& wsAttr)
{
if (oReader.GetAttributesCount() <= 0)
return;
if (!oReader.MoveToFirstAttribute())
return;
CWString wsName = oReader.GetName();
while (!wsName.empty())
{
if (wsName == wsAttrName)
{
wsAttr.create(oReader.GetText(), true);
break;
}
if (!oReader.MoveToNextAttribute())
break;
wsName = oReader.GetName();
}
oReader.MoveToElement();
}
bool VmlToRenderer(const CWString& _wsString, IRenderer* pRenderer)
{
bool bWinding = false;
const wchar_t* wsString = _wsString.c_str();
int nLen = _wsString.size();
int nPos = 0;
double dCurX = 0.0, dCurY = 0.0;
double dCpX = 0.0, dCpY = 0.0;
bool bPrevCommandIsCurve = false;
wchar_t wChar = 0x00;
while (nPos < nLen)
{
if (!SkipWhiteSpacesAndCommas(wsString, nPos, nLen))
break;
wChar = GetChar(wsString, nPos);
switch (wChar)
{
case 'F':
{
if (!SkipWhiteSpacesAndCommas(wsString, nPos, nLen))
break;
bWinding = GetBool(wsString, nPos, nLen);
bPrevCommandIsCurve = false;
break;
}
case 'm':
{
dCurX += GetDouble(wsString, nPos, nLen);
dCurY += GetDouble(wsString, nPos, nLen);
pRenderer->PathCommandMoveTo(xpsUnitToMM(dCurX), xpsUnitToMM(dCurY));
bPrevCommandIsCurve = false;
break;
}
case 'M':
{
dCurX = GetDouble(wsString, nPos, nLen);
dCurY = GetDouble(wsString, nPos, nLen);
pRenderer->PathCommandMoveTo(xpsUnitToMM(dCurX), xpsUnitToMM(dCurY));
bPrevCommandIsCurve = false;
break;
}
case 'L':
case 'l':
case 'H':
case 'h':
case 'V':
case 'v':
{
while (true)
{
if (!SkipWhiteSpacesAndCommas(wsString, nPos, nLen))
break;
if (IsNumber(LookChar(wsString, nPos)))
{
switch (wChar)
{
case 'L':
{
dCurX = GetDouble(wsString, nPos, nLen);
dCurY = GetDouble(wsString, nPos, nLen);
break;
}
case 'l':
{
dCurX += GetDouble(wsString, nPos, nLen);
dCurY += GetDouble(wsString, nPos, nLen);
break;
}
case 'H':
{
dCurX = GetDouble(wsString, nPos, nLen);
break;
}
case 'h':
{
dCurX += GetDouble(wsString, nPos, nLen);
break;
}
case 'V':
{
dCurY = GetDouble(wsString, nPos, nLen);
break;
}
case 'v':
{
dCurY += GetDouble(wsString, nPos, nLen);
break;
}
}
pRenderer->PathCommandLineTo(xpsUnitToMM(dCurX), xpsUnitToMM(dCurY));
}
else
break;
}
bPrevCommandIsCurve = false;
break;
}
case 'A':
case 'a':
{
while (true)
{
if (!SkipWhiteSpacesAndCommas(wsString, nPos, nLen))
break;
if (IsNumber(LookChar(wsString, nPos)))
AppendArcTo(wsString, nPos, nLen, dCurX, dCurY, pRenderer, 'a' == wChar ? true : false);
else
break;
}
bPrevCommandIsCurve = false;
break;
}
case 'C':
case 'c':
{
while (true)
{
if (!SkipWhiteSpacesAndCommas(wsString, nPos, nLen))
break;
if (IsNumber(LookChar(wsString, nPos)))
{
double dX1, dY1, dX2, dY2;
if ('c' == wChar)
{
dX1 = dCurX + GetDouble(wsString, nPos, nLen);
dY1 = dCurY + GetDouble(wsString, nPos, nLen);
dX2 = dCurX + GetDouble(wsString, nPos, nLen);
dY2 = dCurY + GetDouble(wsString, nPos, nLen);
dCurX += GetDouble(wsString, nPos, nLen);
dCurY += GetDouble(wsString, nPos, nLen);
}
else
{
dX1 = GetDouble(wsString, nPos, nLen);
dY1 = GetDouble(wsString, nPos, nLen);
dX2 = GetDouble(wsString, nPos, nLen);
dY2 = GetDouble(wsString, nPos, nLen);
dCurX = GetDouble(wsString, nPos, nLen);
dCurY = GetDouble(wsString, nPos, nLen);
}
pRenderer->PathCommandCurveTo(xpsUnitToMM(dX1), xpsUnitToMM(dY1), xpsUnitToMM(dX2), xpsUnitToMM(dY2), xpsUnitToMM(dCurX), xpsUnitToMM(dCurY));
dCpX = dX2;
dCpY = dY2;
bPrevCommandIsCurve = true;
}
else
break;
}
break;
}
case 'Q':
case 'q':
{
while (true)
{
if (!SkipWhiteSpacesAndCommas(wsString, nPos, nLen))
break;
if (IsNumber(LookChar(wsString, nPos)))
{
double dX1, dY1, dEndX, dEndY;
if ('q' == wChar)
{
dX1 = dCurX + GetDouble(wsString, nPos, nLen);
dY1 = dCurY + GetDouble(wsString, nPos, nLen);
dEndX = dCurX + GetDouble(wsString, nPos, nLen);
dEndY = dCurY + GetDouble(wsString, nPos, nLen);
}
else
{
dX1 = GetDouble(wsString, nPos, nLen);
dY1 = GetDouble(wsString, nPos, nLen);
dEndX = GetDouble(wsString, nPos, nLen);
dEndY = GetDouble(wsString, nPos, nLen);
}
double dCx1 = dCurX / 3 + 2 * dX1 / 3;
double dCy1 = dCurY / 3 + 2 * dY1 / 3;
double dCx2 = dEndX / 3 + 2 * dX1 / 3;
double dCy2 = dEndY / 3 + 2 * dY1 / 3;
pRenderer->PathCommandCurveTo(xpsUnitToMM(dCx1), xpsUnitToMM(dCy1), xpsUnitToMM(dCx2), xpsUnitToMM(dCy2), xpsUnitToMM(dEndX), xpsUnitToMM(dEndY));
dCurX = dEndX;
dCurY = dEndY;
}
else
break;
}
bPrevCommandIsCurve = false;
break;
}
case 'S':
case 's':
{
while (true)
{
if (!SkipWhiteSpacesAndCommas(wsString, nPos, nLen))
break;
if (IsNumber(LookChar(wsString, nPos)))
{
double dX1, dY1, dX2, dY2;
if (!bPrevCommandIsCurve)
{
dX1 = dCurX;
dY1 = dCurY;
}
else
{
dX1 = 2 * dCurX - dCpX;
dY1 = 2 * dCurY - dCpY;
}
if ('s' == wChar)
{
dX2 = dCurX + GetDouble(wsString, nPos, nLen);
dY2 = dCurX + GetDouble(wsString, nPos, nLen);
dCurX += GetDouble(wsString, nPos, nLen);
dCurY += GetDouble(wsString, nPos, nLen);
}
else
{
dX2 = GetDouble(wsString, nPos, nLen);
dY2 = GetDouble(wsString, nPos, nLen);
dCurX = GetDouble(wsString, nPos, nLen);
dCurY = GetDouble(wsString, nPos, nLen);
}
pRenderer->PathCommandCurveTo(xpsUnitToMM(dX1), xpsUnitToMM(dY1), xpsUnitToMM(dX2), xpsUnitToMM(dY2), xpsUnitToMM(dCurX), xpsUnitToMM(dCurY));
dCpX = dX2;
dCpY = dY2;
bPrevCommandIsCurve = true;
}
else
break;
}
break;
}
case 'Z':
case 'z':
{
pRenderer->PathCommandClose();
bPrevCommandIsCurve = false;
break;
}
}
}
return bWinding;
}
bool GetNextGlyph(const wchar_t* wsIndices, int& nIndicesPos, const int& nIndicesLen, unsigned short* pUtf16, int& nUtf16Pos, const int& nUtf16Len, TIndicesEntry& oEntry)
{
oEntry.Reset();
if (!wsIndices || nIndicesPos >= nIndicesLen)
{
if (!pUtf16 || nUtf16Pos >= nUtf16Len)
return false;
oEntry.nUnicode = pUtf16[nUtf16Pos++];
oEntry.bUnicode = true;
return true;
}
if (!SkipWhiteSpaces(wsIndices, nIndicesPos, nIndicesLen))
return false;
wchar_t wChar = LookChar(wsIndices, nIndicesPos);
int nCodeUnitCount = 1, nGlyphCount = 1;
if ('(' == wChar)
{
nIndicesPos++;
if (!SkipWhiteSpaces(wsIndices, nIndicesPos, nIndicesLen))
return false;
wChar = LookChar(wsIndices, nIndicesPos);
if (IsNumber(wChar))
nCodeUnitCount = GetInt(wsIndices, nIndicesPos, nIndicesLen);
else
nCodeUnitCount = 1;
if (!SkipWhiteSpaces(wsIndices, nIndicesPos, nIndicesLen))
return false;
wChar = LookChar(wsIndices, nIndicesPos);
if (':' == wChar)
{
nIndicesPos++;
if (!SkipWhiteSpaces(wsIndices, nIndicesPos, nIndicesLen))
return false;
wChar = LookChar(wsIndices, nIndicesPos);
if (IsNumber(wChar))
nGlyphCount = GetInt(wsIndices, nIndicesPos, nIndicesLen);
else
nGlyphCount = 1;
}
else
return false;
if (!SkipWhiteSpaces(wsIndices, nIndicesPos, nIndicesLen))
return false;
wChar = LookChar(wsIndices, nIndicesPos);
if (')' != wChar)
return false; // Такого не должно быть
nIndicesPos++;
}
else if (oEntry.vRemainUnicodes.size() > 0)
{
nCodeUnitCount = 0;
nGlyphCount = 0;
}
if (nCodeUnitCount > 0 && nGlyphCount > 0)
{
oEntry.vRemainUnicodes.clear();
// Нам нужно прочитать сколько реальных юникодных значений лежит в
// промежутке [pUnicode + nUnicodePos, pUnicode + nUnicodePos + nCodeUnitCount]
int nUnicodesCount = 0;
unsigned int* pUnicodes = NULL;
nCodeUnitCount = std::min(nUtf16Len - nUtf16Pos, nCodeUnitCount);
if (nCodeUnitCount)
{
pUnicodes = new unsigned int[nCodeUnitCount];
if (!pUnicodes)
return false;
unsigned short ushLeading, ushTraling;
unsigned int unCode;
int nCodeUnitPos = 0;
while (nCodeUnitPos < nCodeUnitCount)
{
ushLeading = pUtf16[nUtf16Pos + nCodeUnitPos++];
if (ushLeading < 0xD800 || ushLeading > 0xDFFF)
{
pUnicodes[nUnicodesCount++] = (unsigned int)ushLeading;
}
else if (ushLeading >= 0xDC00)
{
// Такого не должно быть
continue;
}
else
{
if (nCodeUnitPos >= nCodeUnitCount)
break;
unCode = (ushLeading & 0x3FF) << 10;
ushTraling = pUtf16[nUtf16Pos + nCodeUnitPos++];
if (ushTraling < 0xDC00 || ushTraling > 0xDFFF)
{
// Такого не должно быть
continue;
}
else
{
pUnicodes[nUnicodesCount++] = (unCode | (ushTraling & 0x3FF) + 0x10000);
}
}
}
}
// Равномерно распределяем юникоды по глифам, в идеале их количество должно совпадать.
// Если юникодов больше, то лишние удаляем, если их меньше, то недостающие заполняем пробелами.
nUnicodesCount = std::min(nUnicodesCount, nGlyphCount);
for (int nIndex = 0; nIndex < nGlyphCount; nIndex++)
{
if (nIndex < nUnicodesCount)
oEntry.vRemainUnicodes.push_back(pUnicodes[nIndex]);
else
oEntry.vRemainUnicodes.push_back(0x20);
}
if (pUnicodes)
delete[] pUnicodes;
nUtf16Pos += nCodeUnitCount;
}
if (oEntry.vRemainUnicodes.size() <= 0)
return false;
// Теперь мы читаем ровно 1 глиф с возможными метриками
oEntry.nUnicode = oEntry.vRemainUnicodes.at(0);
oEntry.vRemainUnicodes.erase(oEntry.vRemainUnicodes.begin());
wChar = LookChar(wsIndices, nIndicesPos);
if (!SkipWhiteSpaces(wsIndices, nIndicesPos, nIndicesLen))
return false;
if (IsNumber(wChar))
{
oEntry.nGid = GetInt(wsIndices, nIndicesPos, nIndicesLen);
oEntry.bGid = true;
}
else
{
oEntry.bGid = false;
}
if (!SkipWhiteSpaces(wsIndices, nIndicesPos, nIndicesLen))
return true;
wChar = LookChar(wsIndices, nIndicesPos);
if (',' == wChar)
{
nIndicesPos++;
if (!SkipWhiteSpaces(wsIndices, nIndicesPos, nIndicesLen))
return true;
wChar = LookChar(wsIndices, nIndicesPos);
if (IsNumber(wChar))
{
oEntry.dAdvance = GetDouble(wsIndices, nIndicesPos, nIndicesLen);
oEntry.bAdvance = true;
}
else
{
oEntry.bAdvance = false;
}
if (!SkipWhiteSpaces(wsIndices, nIndicesPos, nIndicesLen))
return true;
wChar = LookChar(wsIndices, nIndicesPos);
if (',' == wChar)
{
nIndicesPos++;
if (!SkipWhiteSpaces(wsIndices, nIndicesPos, nIndicesLen))
return true;
wChar = LookChar(wsIndices, nIndicesPos);
if (IsNumber(wChar))
{
oEntry.dHorOffset = GetDouble(wsIndices, nIndicesPos, nIndicesLen);
oEntry.bHorOffset = true;
}
else
{
oEntry.bHorOffset = false;
}
if (!SkipWhiteSpaces(wsIndices, nIndicesPos, nIndicesLen))
return true;
wChar = LookChar(wsIndices, nIndicesPos);
if (',' == wChar)
{
nIndicesPos++;
if (!SkipWhiteSpaces(wsIndices, nIndicesPos, nIndicesLen))
return true;
wChar = LookChar(wsIndices, nIndicesPos);
if (IsNumber(wChar))
{
oEntry.dVerOffset = GetDouble(wsIndices, nIndicesPos, nIndicesLen);
oEntry.bVerOffset = true;
}
else
{
oEntry.bVerOffset = false;
}
}
}
}
if (!SkipWhiteSpaces(wsIndices, nIndicesPos, nIndicesLen))
return true;
wChar = LookChar(wsIndices, nIndicesPos);
if (';' == wChar)
{
nIndicesPos++;
return true;
}
else
return false; // Такого не должно быть
}
void ReadMatrixTransform(XmlUtils::CXmlLiteReader& oReader, CWString& wsTransform, CWString* pwsKey)
{
if (oReader.GetAttributesCount() <= 0)
return;
if (!oReader.MoveToFirstAttribute())
return;
CWString wsAttrName = oReader.GetName();
while (!wsAttrName.empty())
{
if (wsAttrName == L"Matrix")
wsTransform.create(oReader.GetText(), true);
else if (wsAttrName == L"x:Key" && pwsKey)
pwsKey->create(oReader.GetText(), true);
if (!oReader.MoveToNextAttribute())
break;
wsAttrName = oReader.GetName();
}
oReader.MoveToElement();
}
void ReadTransform (XmlUtils::CXmlLiteReader& oReader, CWString& wsTransform, CWString* pwsKey)
{
CWString wsNodeName;
int nCurDepth = oReader.GetDepth();
while (oReader.ReadNextSiblingNode(nCurDepth))
{
wsNodeName = oReader.GetNameNoNS();
if (wsNodeName == L"MatrixTransform")
{
ReadMatrixTransform(oReader, wsTransform, pwsKey);
}
}
}
void ReadPathGeometry (XmlUtils::CXmlLiteReader& oReader, CWString& wsData, CWString& wsTransform, CWString* pwsKey)
{
bool bEvenOdd = true;
CWString wsAttrName;
if (oReader.MoveToFirstAttribute())
{
wsAttrName = oReader.GetName();
while (!wsAttrName.empty())
{
if (wsAttrName == L"x:Key" && pwsKey)
pwsKey->create(oReader.GetText(), true);
else if (wsAttrName == L"Figures")
wsData.create(oReader.GetText(), true);
else if (wsAttrName == L"Transform")
wsTransform.create(oReader.GetText(), true);
else if (wsAttrName == L"FillRule")
{
CWString wsFillingRule = oReader.GetText();
bEvenOdd = wsFillingRule == L"EvenOdd" ? true : false;
}
if (!oReader.MoveToNextAttribute())
break;
wsAttrName = oReader.GetName();
}
oReader.MoveToElement();
}
if (oReader.IsEmptyNode())
return;
std::wstring _wsData;
CWString wsNodeName;
int nCurDepth = oReader.GetDepth();
while (oReader.ReadNextSiblingNode(nCurDepth))
{
wsNodeName = oReader.GetNameNoNS();
if (wsNodeName == L"PathGeometry.Transform" && wsTransform.empty())
ReadTransform(oReader, wsTransform);
else if (wsNodeName == L"PathFigure" && wsData.empty())
ReadPathFigure(oReader, _wsData, bEvenOdd);
}
wsData.create(_wsData.c_str(), true);
}
void ReadPathFigure (XmlUtils::CXmlLiteReader& oReader, std::wstring& wsData, bool bEvenOdd)
{
// TODO: Улучшить здесь сложение строк и хождение по атрибутам
if (oReader.IsEmptyNode())
return;
if (!bEvenOdd)
wsData += L"F 1";
std::wstring wsStartPoint;
std::wstring wsIsClosed;
ReadAttribute(oReader, L"StartPoint", wsStartPoint);
ReadAttribute(oReader, L"IsClosed", wsIsClosed);
wsData += L" M " + wsStartPoint;
std::wstring wsNodeName;
std::wstring wsText;
int nCurDepth = oReader.GetDepth();
while (oReader.ReadNextSiblingNode(nCurDepth))
{
wsNodeName = oReader.GetNameNoNS();
wsText.empty();
if (L"PolyLineSegment" == wsNodeName)
{
ReadAttribute(oReader, L"Points", wsText);
wsData += L" L " + wsText;
}
else if (L"PolyBezierSegment" == wsNodeName)
{
ReadAttribute(oReader, L"Points", wsText);
wsData += L" C " + wsText;
}
else if (L"PolyQuadraticBezierSegment" == wsNodeName)
{
ReadAttribute(oReader, L"Points", wsText);
wsData += L" Q " + wsText;
}
else if (L"ArcSegment" == wsNodeName)
{
std::wstring wsSize, wsRotationAngle, wsIsLargeArc, wsSweepDirection, wsPoint, wsIsStroked;
if (oReader.MoveToFirstAttribute())
{
std::wstring wsAttrName = oReader.GetName();
while (!wsAttrName.empty())
{
if (L"Size" == wsAttrName)
wsSize = oReader.GetText();
else if (L"RotationAngle" == wsAttrName)
wsRotationAngle = oReader.GetText();
else if (L"IsLargeArc" == wsAttrName)
wsIsLargeArc = oReader.GetText();
else if (L"SweepDirection" == wsAttrName)
wsSweepDirection = oReader.GetText();
else if (L"Point" == wsAttrName)
wsPoint = oReader.GetText();
if (!oReader.MoveToNextAttribute())
break;
wsAttrName = oReader.GetName();
}
oReader.MoveToElement();
}
wsData += L" A " + wsSize + L" " + wsRotationAngle + L" ";
if (GetBool(wsIsLargeArc))
wsData += L"0 ";
else
wsData += L"1 ";
if (L"Counterclockwise" == wsSweepDirection)
wsData += L"0 ";
else
wsData += L"1 ";
wsData += wsPoint;
}
}
if (GetBool(wsIsClosed))
wsData += L" Z ";
}
void ReadGradientStops (XmlUtils::CXmlLiteReader& oReader, std::vector<LONG>& vColors, std::vector<double>& vPositions, const double& dOpacity)
{
if (oReader.IsEmptyNode())
return;
CWString wsNodeName, wsAttrName;
int nCurDepth = oReader.GetDepth();
while (oReader.ReadNextSiblingNode(nCurDepth))
{
wsNodeName = oReader.GetNameNoNS();
if (wsNodeName == L"GradientStop")
{
double dPos = 0;
LONG lColor = 0;
if (oReader.MoveToFirstAttribute())
{
wsAttrName = oReader.GetName();
while (!wsAttrName.empty())
{
if (wsAttrName == L"Color")
{
int nBgr, nAlpha;
ReadSTColor(oReader.GetText(), nBgr, nAlpha);
nAlpha *= dOpacity;
lColor = (nAlpha << 24 & 0xFF000000) | (nBgr & 0xFFFFFF);
}
else if (wsAttrName == L"Offset")
{
ReadSTDouble(oReader.GetText(), dPos);
}
if (!oReader.MoveToNextAttribute())
break;
wsAttrName = oReader.GetName();
}
oReader.MoveToElement();
}
vColors.push_back(lColor);
vPositions.push_back(dPos);
}
}
}
void ReadClip (XmlUtils::CXmlLiteReader& oReader, CWString& wsClip)
{
CWString wsNodeName;
int nCurDepth = oReader.GetDepth();
while (oReader.ReadNextSiblingNode(nCurDepth))
{
wsNodeName = oReader.GetNameNoNS();
if (wsNodeName == L"PathGeometry")
{
CWString wsTransform;
ReadPathGeometry(oReader, wsClip, wsTransform);
}
}
}
void ReadSTPoint(const CWString& wsString, double& dX, double& dY)
{
int nCommaPos = 0;
while (nCommaPos < wsString.size())
{
if (wsString[nCommaPos] == ',')
break;
nCommaPos++;
}
if (nCommaPos >= wsString.size())
{
CWString wsX = wsString.c_str();
dX = GetDouble(wsX);
dY = 0;
}
else
{
CWString wsX((wchar_t*)wsString.c_str(), false, nCommaPos);
CWString wsY((wchar_t*)(wsString.c_str() + nCommaPos + 1), false, wsString.size() - nCommaPos - 1);
dX = GetDouble(wsX);
dY = GetDouble(wsY);
}
}
void ReadSTColor(const CWString& wsString, int& nBgr, int& nAlpha)
{
nBgr = 0;
nAlpha = 255;
int nLen = wsString.size();
if (nLen <= 0)
return;
const wchar_t* pBuffer = wsString.c_str();
if (L'#' == pBuffer[0])
{
nLen--;
pBuffer++;
if (6 != nLen && 8 != nLen)
return;
if (8 == nLen)
{
nAlpha = GetDigit(*pBuffer++);
nAlpha <<= 4;
nAlpha += GetDigit(*pBuffer++);
}
else
{
nAlpha = 255;
}
nBgr = GetDigit(pBuffer[4]);
nBgr <<= 4;
nBgr += GetDigit(pBuffer[5]);
nBgr <<= 4;
nBgr += GetDigit(pBuffer[2]);
nBgr <<= 4;
nBgr += GetDigit(pBuffer[3]);
nBgr <<= 4;
nBgr += GetDigit(pBuffer[0]);
nBgr <<= 4;
nBgr += GetDigit(pBuffer[1]);
}
else if (nLen >= 3 && L's' == pBuffer[0] && L'c' == pBuffer[1] && L'#' == pBuffer[2])
{
int nPos = 3;
if (nPos >= nLen)
return;
CWString wsString2;
wsString2.create(pBuffer + 3, false);
std::vector<CWString> vElements = wsString2.split(',');
if (3 == vElements.size())
{
nAlpha = 255;
nBgr = (((int)(std::min(GetDouble(vElements[2]), 1.0) * 255)) << 16) + (((int)(std::min(GetDouble(vElements[1]), 1.0) * 255)) << 8) + ((int)(std::min(GetDouble(vElements[0]), 1.0) * 255));
}
else if (4 == vElements.size())
{
nAlpha = (int)(std::min(GetDouble(vElements[0]), 1.0) * 255);
nBgr = (((int)(std::min(GetDouble(vElements[3]), 1.0) * 255)) << 16) + (((int)(std::min(GetDouble(vElements[2]), 1.0) * 255)) << 8) + ((int)(std::min(GetDouble(vElements[1]), 1.0) * 255));
}
}
}
void ReadSTColor(const CWString& wsString, LONG& lColor)
{
int nBgr, nAlpha;
ReadSTColor(wsString, nBgr, nAlpha);
lColor = (nAlpha << 24 & 0xFF000000) | (nBgr & 0xFFFFFF);
}
void ReadSTDouble(const CWString& wsString, double& dValue)
{
int nPos = 0;
dValue = GetDouble(wsString.c_str(), nPos, wsString.size());
}
}