Files
core/XpsFile/XpsLib/Utils.cpp
2026-04-02 08:10:57 +03:00

1748 lines
46 KiB
C++

/*
* (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)
{
// Function for converting a real angle to parametric ellipse representation
// i.e. x = a cos(t) y = b sin(t) - parametric ellipse representation.
// 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)
{
// Calculate start, end and control points
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)
{
// Convert angles to radians
double dAngle1 = fAngle1 * 3.141592 / 180;
double dAngle2 = fAngle2 * 3.141592 / 180;
// Determine in which quadrants the start and end points are located
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));
// Draw a line to the starting point of the arc
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);
// Next we draw by quadrants
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) // Full ellipse
{
Ellipse(pRenderer, oTransform, fX, fY, fXRad, fYRad);
}
else // Ellipse arc
{
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);
// Get the quadratic equation A2 * X^2 + B2 * X + C2 = 0 (where A2 != 0 in our case)
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 is always positive (so we can safely divide by it)
double dK2 = 2 * dA1 * dB1;
double dK3 = SQR(dB1);
double dK4 = dK3 - SQR(dK2) / (4 * dK1);
if (dK4 < 0)
return false; // This should not happen
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)
{
// This should not happen
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;
// Centers of the sought ellipses lie on a vertical line
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)
{
// This should not happen
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)
{
// Determine the quadrant
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;
}
// Move point X0, Y0 to the origin and rotate by the inverse angle so that the sought ellipses are positioned correctly
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;
// Find ellipses whose intersection points are (0, 0) and (dX1, dY1)
if (!XPSEllipse::GetEllipses(dX1, dY1, dRadX, dRadY, dCx1, dCy1, dCx2, dCy2))
{
// This should not happen
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);
// Now we have 2 ellipses, we need to determine the arc that we need to draw.
// First, let's find the angles of the start and end points for both ellipses.
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;
}
// Approximate the ellipse with Bezier curves
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; // This should not happen
nIndicesPos++;
}
else if (oEntry.vRemainUnicodes.size() > 0)
{
nCodeUnitCount = 0;
nGlyphCount = 0;
}
if (nCodeUnitCount > 0 && nGlyphCount > 0)
{
oEntry.vRemainUnicodes.clear();
// We need to read how many real unicode values are in the
// range [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)
{
// This should not happen
continue;
}
else
{
if (nCodeUnitPos >= nCodeUnitCount)
break;
unCode = (ushLeading & 0x3FF) << 10;
ushTraling = pUtf16[nUtf16Pos + nCodeUnitPos++];
if (ushTraling < 0xDC00 || ushTraling > 0xDFFF)
{
// This should not happen
continue;
}
else
{
pUnicodes[nUnicodesCount++] = (unCode | (ushTraling & 0x3FF) + 0x10000);
}
}
}
}
// Evenly distribute unicodes across glyphs, ideally their count should match.
// If there are more unicodes, we remove the extra ones; if there are fewer, we fill the missing ones with spaces.
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;
// Now we read exactly 1 glyph with possible metrics
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; // This should not happen
}
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: Improve string concatenation and attribute traversal here
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());
}
}