diff --git a/XpsFile/XpsFile.cpp b/XpsFile/XpsFile.cpp index 0143c2ad82..51dab5cde8 100644 --- a/XpsFile/XpsFile.cpp +++ b/XpsFile/XpsFile.cpp @@ -1,4 +1,4 @@ -#include "XpsFile.h" +#include "XpsFile.h" #include "XpsLib/Document.h" #include "../DesktopEditor/common/File.h" @@ -16,7 +16,7 @@ CXpsFile::CXpsFile(CApplicationFonts* pAppFonts) { m_pAppFonts = pAppFonts; - // + // Создаем менеджер шрифтов с собственным кэшем m_pFontManager = pAppFonts->GenerateFontManager(); CFontsCache* pMeasurerCache = new CFontsCache(); pMeasurerCache->SetStreams(pAppFonts->GetStreams()); @@ -55,7 +55,7 @@ bool CXpsFile::LoadFromFile(const std::wstring& wsSrcFileName, const std { Close(); - // Zip- + // Распаковываем Zip-архив в темповую папку COfficeUtils oUtils(NULL); if (S_OK != oUtils.ExtractToDirectory(wsSrcFileName, m_wsTempFolder, NULL, 0)) return false; diff --git a/XpsFile/XpsFile.h b/XpsFile/XpsFile.h index 92d7bc84e5..13dbbe143c 100644 --- a/XpsFile/XpsFile.h +++ b/XpsFile/XpsFile.h @@ -1,4 +1,4 @@ -#ifndef _XPS_FILE_H +#ifndef _XPS_FILE_H #define _XPS_FILE_H #include diff --git a/XpsFile/XpsFileTest/XpsFileTest.cpp b/XpsFile/XpsFileTest/XpsFileTest.cpp index dfc207bfa5..64b3047fc2 100644 --- a/XpsFile/XpsFileTest/XpsFileTest.cpp +++ b/XpsFile/XpsFileTest/XpsFileTest.cpp @@ -102,13 +102,13 @@ void main() { clock_t oBeginTime = clock(); - //ConvertFolderToRaster(L"D:/Test Files//Xps//"); + ConvertFolderToRaster(L"D:/Test Files//Xps//"); ConvertFolderToPdf(L"D:/Test Files//Xps//"); clock_t oEndTime = clock(); double dElapsedSecs = double(oEndTime - oBeginTime) / CLOCKS_PER_SEC; printf("%fseconds\n", dElapsedSecs); - char q; - std::cin >> q; + //char q; + //std::cin >> q; } diff --git a/XpsFile/XpsLib/ContextState.cpp b/XpsFile/XpsLib/ContextState.cpp index e84cc221d4..0428bb5dc1 100644 --- a/XpsFile/XpsLib/ContextState.cpp +++ b/XpsFile/XpsLib/ContextState.cpp @@ -1,4 +1,4 @@ -#include "ContextState.h" +#include "ContextState.h" #include "StaticResources.h" #ifndef xpsUnitToMM diff --git a/XpsFile/XpsLib/ContextState.h b/XpsFile/XpsLib/ContextState.h index abc50a7740..25ecfece5f 100644 --- a/XpsFile/XpsLib/ContextState.h +++ b/XpsFile/XpsLib/ContextState.h @@ -1,4 +1,4 @@ -#ifndef _XPS_XPSLIB_CONTEXTSTATE_H +#ifndef _XPS_XPSLIB_CONTEXTSTATE_H #define _XPS_XPSLIB_CONTEXTSTATE_H #include "Utils.h" diff --git a/XpsFile/XpsLib/Document.cpp b/XpsFile/XpsLib/Document.cpp index e2c33bf3a4..3132052116 100644 --- a/XpsFile/XpsLib/Document.cpp +++ b/XpsFile/XpsLib/Document.cpp @@ -1,7 +1,8 @@ -#include "Document.h" +#include "Document.h" #include "StaticResources.h" #include "../../Common/DocxFormat/Source/XML/xmlutils.h" +#include "../../DesktopEditor/common/File.h" namespace XPS { @@ -23,16 +24,9 @@ namespace XPS XmlUtils::CXmlLiteReader oReader; std::wstring wsRelsPath = NormalizePath(wsPath + L"_rels/.rels"); - - clock_t oBeginTime = clock(); - if (!oReader.FromFile(wsRelsPath)) return false; - clock_t oEndTime = clock(); - double dElapsedSecs = double(oEndTime - oBeginTime) / CLOCKS_PER_SEC; - printf("%S %fseconds\n", wsRelsPath.c_str(), dElapsedSecs); - if (!oReader.ReadNextNode()) return false; @@ -40,7 +34,7 @@ namespace XPS if (L"Relationships" != wsName) return false; - std::wstring wsFile; + std::wstring wsTargetFile; while (oReader.ReadNextNode()) { wsName = oReader.GetName(); @@ -51,26 +45,28 @@ namespace XPS if (L"http://schemas.microsoft.com/xps/2005/06/fixedrepresentation" == wsAttr) { - ReadAttribute(oReader, L"Target", wsFile); + ReadAttribute(oReader, L"Target", wsTargetFile); break; } } } - if (wsFile.empty()) + if (wsTargetFile.empty()) return false; oReader.Clear(); - oBeginTime = clock(); - - if (!oReader.FromFile(wsPath + wsFile)) + std::wstring wsTargerFullPath = m_wsPath + wsTargetFile; + if (!NSFile::CFileBinary::Exists(wsTargerFullPath)) + { + wsTargerFullPath = GetPath(wsRelsPath) + wsTargetFile; + if (!NSFile::CFileBinary::Exists(wsTargerFullPath)) + return false; + } + + if (!oReader.FromFile(wsTargerFullPath)) return false; - oEndTime = clock(); - dElapsedSecs = double(oEndTime - oBeginTime) / CLOCKS_PER_SEC; - printf("%S %fseconds\n", (wsPath + wsFile).c_str(), dElapsedSecs); - if (!oReader.ReadNextNode()) return false; @@ -78,31 +74,34 @@ namespace XPS if (L"FixedDocumentSequence" != wsName) return false; - wsFile.clear(); + std::wstring wsSourceFile; while (oReader.ReadNextNode()) { wsName = oReader.GetName(); if (L"DocumentReference" == wsName) { - ReadAttribute(oReader, L"Source", wsFile); + ReadAttribute(oReader, L"Source", wsSourceFile); break; } } - if (wsFile.empty()) + if (wsSourceFile.empty()) return false; oReader.Clear(); - oBeginTime = clock(); - if (!oReader.FromFile(m_wsPath + wsFile)) + std::wstring wsSourceFullPath = m_wsPath + wsSourceFile; + if (!NSFile::CFileBinary::Exists(wsSourceFullPath)) + { + wsSourceFullPath = GetPath(wsTargerFullPath) + wsSourceFile; + if (!NSFile::CFileBinary::Exists(wsSourceFullPath)) + return false; + } + + if (!oReader.FromFile(wsSourceFullPath)) return false; - oEndTime = clock(); - dElapsedSecs = double(oEndTime - oBeginTime) / CLOCKS_PER_SEC; - printf("%S %fseconds\n", (m_wsPath + wsFile).c_str(), dElapsedSecs); - if (!oReader.ReadNextNode()) return false; @@ -110,7 +109,7 @@ namespace XPS if (L"FixedDocument" != wsName) return false; - std::wstring wsFilePath = GetPath(m_wsPath + wsFile); + std::wstring wsFilePath = GetPath(wsSourceFullPath); std::wstring wsPagePath; std::wstring wsSource; @@ -122,10 +121,14 @@ namespace XPS if (L"PageContent" == wsName) { ReadAttribute(oReader, L"Source", wsSource); - if ('/' == wsSource[0]) - wsPagePath = m_wsPath + wsSource; - else + + std::wstring wsPagePath = m_wsPath + wsSource; + if (!NSFile::CFileBinary::Exists(wsPagePath)) + { wsPagePath = wsFilePath + wsSource; + if (!NSFile::CFileBinary::Exists(wsPagePath)) + continue; + } m_mPages.insert(std::pair(nIndex++, new XPS::Page(wsPagePath, wsPath, &m_oFontList, m_pFontManager, this))); } diff --git a/XpsFile/XpsLib/Document.h b/XpsFile/XpsLib/Document.h index 5a3396ca5d..0f294d6681 100644 --- a/XpsFile/XpsLib/Document.h +++ b/XpsFile/XpsLib/Document.h @@ -1,4 +1,4 @@ -#ifndef _XPS_XPSLIB_DOCUMENT_H +#ifndef _XPS_XPSLIB_DOCUMENT_H #define _XPS_XPSLIB_DOCUMENT_H #include "FontList.h" diff --git a/XpsFile/XpsLib/FontList.h b/XpsFile/XpsLib/FontList.h index 9d99417691..331a44e88e 100644 --- a/XpsFile/XpsLib/FontList.h +++ b/XpsFile/XpsLib/FontList.h @@ -1,4 +1,4 @@ -#ifndef _XPS_XPSLIB_FONTLIST_H +#ifndef _XPS_XPSLIB_FONTLIST_H #define _XPS_XPSLIB_FONTLIST_H #include diff --git a/XpsFile/XpsLib/Page.cpp b/XpsFile/XpsLib/Page.cpp index 4e253506ab..0203855bd7 100644 --- a/XpsFile/XpsLib/Page.cpp +++ b/XpsFile/XpsLib/Page.cpp @@ -1,4 +1,4 @@ -#include "Page.h" +#include "Page.h" #include #include "../../DesktopEditor/common/String.h" #include "../../DesktopEditor/graphics/structures.h" @@ -548,9 +548,9 @@ namespace XPS return; } - // , , - // , , - // . + // Сначала задается матрица преобразования, потом клип, потому что даже + // если преобразование задано в дочерней ноде, а клип задан в атрибутах данной ноды, + // то преобразование влияется на клип все равно. if (!wsTransform.empty()) { bTransform = TransformToRenderer(wsTransform.c_str(), pState); @@ -656,7 +656,7 @@ namespace XPS CWString Page::ReadClip(XmlUtils::CXmlLiteReader& oReader) { CWString wsClip; - // TODO: Clip + // TODO: Реализовать чтение Clip return wsClip; } void Page::DrawPath(XmlUtils::CXmlLiteReader& oReader, IRenderer* pRenderer, CContextState* pState) @@ -843,9 +843,9 @@ namespace XPS RELEASEOBJECT(pBrush); } - // , , - // , , - // . + // Сначала задается матрица преобразования, потом клип, потому что даже + // если преобразование задано в дочерней ноде, а клип задан в атрибутах данной ноды, + // то преобразование влияется на клип все равно. if (!wsTransform.empty()) { bTransform = TransformToRenderer(wsTransform.c_str(), pState); diff --git a/XpsFile/XpsLib/Page.h b/XpsFile/XpsLib/Page.h index 14aa41afc8..87e70ebc48 100644 --- a/XpsFile/XpsLib/Page.h +++ b/XpsFile/XpsLib/Page.h @@ -1,4 +1,4 @@ -#ifndef _XPS_XPSLIB_PAGE_H +#ifndef _XPS_XPSLIB_PAGE_H #define _XPS_XPSLIB_PAGE_H #include "../../DesktopEditor/graphics/IRenderer.h" diff --git a/XpsFile/XpsLib/StaticResources.cpp b/XpsFile/XpsLib/StaticResources.cpp index 63f58d60b3..30317b24e5 100644 --- a/XpsFile/XpsLib/StaticResources.cpp +++ b/XpsFile/XpsLib/StaticResources.cpp @@ -1,4 +1,4 @@ -#include "StaticResources.h" +#include "StaticResources.h" #include "../../Common/DocxFormat/Source/XML/xmlutils.h" #include "../../DesktopEditor/graphics/IRenderer.h" @@ -383,8 +383,8 @@ namespace XPS { CBrush* pBrush = NULL; - // TODO: 1. , - // 2. VisualBrush + // TODO: 1. Трансформы здесь не реализованы, потому что их невозможно учесть с текущим интерфейсом рендерера + // 2. Не реализован VisualBrush if (!oReader.IsEmptyNode()) { CWString wsNodeName; diff --git a/XpsFile/XpsLib/StaticResources.h b/XpsFile/XpsLib/StaticResources.h index f4f2e7f0ab..8588a52038 100644 --- a/XpsFile/XpsLib/StaticResources.h +++ b/XpsFile/XpsLib/StaticResources.h @@ -1,4 +1,4 @@ -#ifndef _XPS_XPSLIB_STATICRESOURCES_H +#ifndef _XPS_XPSLIB_STATICRESOURCES_H #define _XPS_XPSLIB_STATICRESOURCES_H #include "Utils.h" diff --git a/XpsFile/XpsLib/Utils.cpp b/XpsFile/XpsLib/Utils.cpp index cc7710b8bd..baa6fa40d6 100644 --- a/XpsFile/XpsLib/Utils.cpp +++ b/XpsFile/XpsLib/Utils.cpp @@ -1,4 +1,4 @@ -#include "Utils.h" +#include "Utils.h" #include "../../DesktopEditor/common/String.h" #include "../../DesktopEditor/common/Types.h" #include "../../Common/DocxFormat/Source/XML/xmlutils.h" @@ -31,8 +31,330 @@ #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 = min(4, max(1, nSecondPointQuard)); + nFirstPointQuard = min(4, max(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; + } + } + 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) @@ -255,100 +577,98 @@ namespace XPS return false; } - static inline void PageCommandA(const wchar_t* wsString, int& nPos, const int& nLen, double& dCurX, double& dCurY, IRenderer* pRenderer) + static inline void AppendArcTo(const wchar_t* wsString, int& nPos, const int& nLen, double& dCurX, double& dCurY, IRenderer* pRenderer,bool bRelative) { - Aggplus::CMatrix tmatx, rmatx, smatx, itmatx, irmatx, ismatx; - double x_cur_temp = dCurX; - double y_cur_temp = dCurY; + double dRadX = GetDouble(wsString, nPos, nLen); + double dRadY = GetDouble(wsString, nPos, nLen); + double dAngle = GetDouble(wsString, nPos, nLen); - double rx = GetDouble(wsString, nPos, nLen); - double ry = GetDouble(wsString, nPos, nLen); - double angle = GetDouble(wsString, nPos, nLen); + bool bIsLarge = !GetBool(wsString, nPos, nLen); + bool bClockwise = !GetBool(wsString, nPos, nLen); - bool isLarge = GetBool(wsString, nPos, nLen); - bool isCCW = !GetBool(wsString, nPos, nLen); + double dX0 = dCurX; + double dY0 = dCurY; + double dX1 = GetDouble(wsString, nPos, nLen); + double dY1 = GetDouble(wsString, nPos, nLen); - double x_end = GetDouble(wsString, nPos, nLen); - double y_end = GetDouble(wsString, nPos, nLen); - - tmatx.Translate(-dCurX, -dCurY); - tmatx.TransformPoint(x_cur_temp, y_cur_temp); - tmatx.TransformPoint(x_end, y_end); - - rmatx.Rotate(-angle); - rmatx.TransformPoint(x_cur_temp, y_cur_temp); - rmatx.TransformPoint(x_end, y_end); - - smatx.Scale(ry / rx, 1); - smatx.TransformPoint(x_cur_temp, y_cur_temp); - smatx.TransformPoint(x_end, y_end); - - double mid_x = (x_cur_temp + x_end) / 2.0, mid_y = (y_cur_temp + y_end) / 2.0; - double vect_x = x_end - x_cur_temp, vect_y = y_end - y_cur_temp; - double length = sqrt(vect_x * vect_x + vect_y * vect_y); - double halfChord = length / 2.0; - double rotated_x, rotated_y; - - if (isLarge == isCCW) + if (bRelative) { - rotated_x = -vect_y; - rotated_y = vect_x; + 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 { - rotated_x = vect_y; - rotated_y = -vect_x; + dCx = dCx2; + dCy = dCy2; + dAngleStart = dAngleStart2; + dAngleEnd = dAngleEnd2; } - rotated_x = rotated_x / length; - rotated_y = rotated_y / length; - - double centerDistance = sqrt(max(ry * ry - halfChord * halfChord, 0)); - double center_x = mid_x + centerDistance * rotated_x, center_y = mid_y + centerDistance * rotated_y; - - double angle1 = atan2(y_cur_temp - center_y, x_cur_temp - center_x); - double angle2 = atan2(y_end - center_y, x_end - center_x); - - if ((!isCCW) && (abs(y_end - center_y) < 0.000001) && (x_end - center_x < 0)) - angle2 = -M_PI; - if ((isCCW) && (abs(y_cur_temp - center_y) < 0.000001) && (x_cur_temp - center_x < 0)) - angle1 = -M_PI; - - if (isLarge == (abs(angle2 - angle1) < M_PI)) - { - if (angle1 < angle2) - angle1 += 2 * M_PI; - else - angle2 += 2 * M_PI; - } - if (abs(angle2 - angle1) == M_PI) - { - if ((angle1 < angle2) && (isCCW) && (angle1 != -M_PI) && (angle1 != 0.0) && (angle1 != M_PI)) - angle1 += 2 * M_PI; - } - - itmatx.Translate(dCurX, dCurY); - irmatx.Rotate(angle); - ismatx.Scale(rx / ry, 1); - - int max = (int)((4 * (rx + ry) * abs(angle2 - angle1) / (2 * M_PI)) / 1/*tolerance*/); - - double x, y; - for (int counter = 0; counter <= max; counter++) - { - double angle_cur = ((max - counter) * angle1 + counter * angle2) / max; - x = center_x + ry * cos(angle_cur); - y = center_y + ry * sin(angle_cur); - - // Transform the point back - ismatx.TransformPoint(x, y); - irmatx.TransformPoint(x, y); - itmatx.TransformPoint(x, y); - pRenderer->PathCommandLineTo(xpsUnitToMM(x), xpsUnitToMM(y)); - } - - dCurX = x; - dCurY = y; + // Аппроксимируем эллипс кривыми Безье + XPSEllipse::AppendEllipseArc(pRenderer, oInverse, dCx, dCy, dRadX, dRadY, dAngleStart, dAngleEnd, bClockwise); + dCurX = _dX1; + dCurY = _dY1; } } @@ -636,8 +956,8 @@ namespace XPS } case 'l': { - dCurX = GetDouble(wsString, nPos, nLen); - dCurY = GetDouble(wsString, nPos, nLen); + dCurX += GetDouble(wsString, nPos, nLen); + dCurY += GetDouble(wsString, nPos, nLen); break; } case 'H': @@ -680,7 +1000,7 @@ namespace XPS break; if (IsNumber(LookChar(wsString, nPos))) - PageCommandA(wsString, nPos, nLen, dCurX, dCurY, pRenderer); + AppendArcTo(wsString, nPos, nLen, dCurX, dCurY, pRenderer, 'a' == wChar ? true : false); else break; } @@ -873,7 +1193,7 @@ namespace XPS wChar = LookChar(wsIndices, nIndicesPos); if (')' != wChar) - return false; // + return false; // Такого не должно быть nIndicesPos++; } @@ -882,8 +1202,8 @@ namespace XPS if (nCodeUnitCount > 0 && nGlyphCount > 0) { oEntry.vRemainUnicodes.clear(); - // - // [pUnicode + nUnicodePos, pUnicode + nUnicodePos + nCodeUnitCount] + // Нам нужно прочитать сколько реальных юникодных значений лежит в + // промежутке [pUnicode + nUnicodePos, pUnicode + nUnicodePos + nCodeUnitCount] int nUnicodesCount = 0; unsigned int* pUnicodes = NULL; nCodeUnitCount = min(nUtf16Len - nUtf16Pos, nCodeUnitCount); @@ -905,7 +1225,7 @@ namespace XPS } else if (ushLeading >= 0xDC00) { - // + // Такого не должно быть continue; } else @@ -917,7 +1237,7 @@ namespace XPS ushTraling = pUtf16[nUtf16Pos + nCodeUnitPos++]; if (ushTraling < 0xDC00 || ushTraling > 0xDFFF) { - // + // Такого не должно быть continue; } else @@ -928,8 +1248,8 @@ namespace XPS } } - // , . - // , , , . + // Равномерно распределяем юникоды по глифам, в идеале их количество должно совпадать. + // Если юникодов больше, то лишние удаляем, если их меньше, то недостающие заполняем пробелами. nUnicodesCount = min(nUnicodesCount, nGlyphCount); for (int nIndex = 0; nIndex < nGlyphCount; nIndex++) { @@ -948,7 +1268,7 @@ namespace XPS if (oEntry.vRemainUnicodes.size() <= 0) return false; - // 1 + // Теперь мы читаем ровно 1 глиф с возможными метриками oEntry.nUnicode = oEntry.vRemainUnicodes.at(0); oEntry.vRemainUnicodes.erase(oEntry.vRemainUnicodes.begin()); @@ -1043,7 +1363,7 @@ namespace XPS return true; } else - return false; // + return false; // Такого не должно быть } void ReadTransform (XmlUtils::CXmlLiteReader& oReader, CWString& wsTransform, CWString* pwsKey) { @@ -1111,6 +1431,7 @@ namespace XPS if (oReader.IsEmptyNode()) return; + std::wstring _wsData; CWString wsNodeName; int nCurDepth = oReader.GetDepth(); while (oReader.ReadNextSiblingNode(nCurDepth)) @@ -1119,14 +1440,13 @@ namespace XPS if (wsNodeName == L"PathGeometry.Transform" && wsTransform.empty()) ReadTransform(oReader, wsTransform); else if (wsNodeName == L"PathFigure" && wsData.empty()) - ReadPathFigure(oReader, wsData, bEvenOdd); + ReadPathFigure(oReader, _wsData, bEvenOdd); } + wsData.create(_wsData.c_str(), true); } - void ReadPathFigure (XmlUtils::CXmlLiteReader& oReader, CWString& _wsData, bool bEvenOdd) + void ReadPathFigure (XmlUtils::CXmlLiteReader& oReader, std::wstring& wsData, bool bEvenOdd) { - // TODO: - std::wstring wsData; - + // TODO: Улучшить здесь сложение строк и хождение по атрибутам if (oReader.IsEmptyNode()) return; @@ -1161,7 +1481,7 @@ namespace XPS } else if (L"ArcSegment" == wsNodeName) { - std::wstring wsSize, wsRotationAngle, wsIsLargeArc, wsSweepDirection, wsPoint; + std::wstring wsSize, wsRotationAngle, wsIsLargeArc, wsSweepDirection, wsPoint, wsIsStroked; if (oReader.MoveToFirstAttribute()) { std::wstring wsAttrName = oReader.GetName(); @@ -1188,9 +1508,9 @@ namespace XPS wsData += L" A " + wsSize + L" " + wsRotationAngle + L" "; if (GetBool(wsIsLargeArc)) - wsData += L"1 "; - else wsData += L"0 "; + else + wsData += L"1 "; if (L"Counterclockwise" == wsSweepDirection) wsData += L"0 "; @@ -1205,8 +1525,6 @@ namespace XPS ReadAttribute(oReader, L"IsClosed", wsClosed); if (GetBool(wsClosed)) wsData += L" Z "; - - _wsData.create(wsData.c_str(), true); } void ReadGradientStops(XmlUtils::CXmlLiteReader& oReader, std::vector& vColors, std::vector& vPositions, const double& dOpacity) { diff --git a/XpsFile/XpsLib/Utils.h b/XpsFile/XpsLib/Utils.h index 3e109c7e07..ad19023529 100644 --- a/XpsFile/XpsLib/Utils.h +++ b/XpsFile/XpsLib/Utils.h @@ -1,4 +1,4 @@ -#ifndef _XPS_XPSLIB_UTILS_H +#ifndef _XPS_XPSLIB_UTILS_H #define _XPS_XPSLIB_UTILS_H #include @@ -85,7 +85,7 @@ namespace XPS void ReadTransform (XmlUtils::CXmlLiteReader& oReader, CWString& wsTransform, CWString* pwsKey = NULL); void ReadPathGeometry(XmlUtils::CXmlLiteReader& oReader, CWString& wsData, CWString& wsTransform, CWString* pwsKey = NULL); - void ReadPathFigure (XmlUtils::CXmlLiteReader& oReader, CWString& _wsData, bool bEvenOdd); + void ReadPathFigure (XmlUtils::CXmlLiteReader& oReader, std::wstring&, bool bEvenOdd); void ReadGradientStops(XmlUtils::CXmlLiteReader& oReader, std::vector& vColors, std::vector& vPositions, const double& dOpacity); } diff --git a/XpsFile/XpsLib/WString.cpp b/XpsFile/XpsLib/WString.cpp index d10fd89608..256c1c33b7 100644 --- a/XpsFile/XpsLib/WString.cpp +++ b/XpsFile/XpsLib/WString.cpp @@ -1,4 +1,4 @@ -#include "WString.h" +#include "WString.h" #include "Utils.h" #include "../../DesktopEditor/common/String.h" diff --git a/XpsFile/XpsLib/WString.h b/XpsFile/XpsLib/WString.h index 800a2e3615..da6a7aadbd 100644 --- a/XpsFile/XpsLib/WString.h +++ b/XpsFile/XpsLib/WString.h @@ -1,4 +1,4 @@ -#ifndef _XPS_XPSLIB_WSTRING_H +#ifndef _XPS_XPSLIB_WSTRING_H #define _XPS_XPSLIB_WSTRING_H #include