/* * (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 "XpsPage.h" #include #include "../../DesktopEditor/common/StringExt.h" #include "../../DesktopEditor/graphics/structures.h" #include "Document.h" #include "StaticResources.h" //#include #ifndef M_PI #define M_PI 3.14159265358979323846 #endif #ifndef xpsUnitToMM #define xpsUnitToMM(x) ((x) * 25.4 / 96) #endif #define IsFromResource(String) (!String.empty() && '{' == String[0]) namespace XPS { static double GetAdvanceX(NSFonts::IFontManager* pFontManager, const unsigned int& unUnicode, const unsigned int& unGid, const bool& bGid) { if (bGid) { pFontManager->SetStringGID(TRUE); return pFontManager->MeasureChar2(unGid).fAdvanceX; } else { pFontManager->SetStringGID(FALSE); return pFontManager->MeasureChar2(unUnicode).fAdvanceX; } } static double GetAdvanceY(NSFonts::IFontManager* pFontManager, const unsigned int& unUnicode, const unsigned int& unGid, const bool& bGid) { if (bGid) { pFontManager->SetStringGID(TRUE); return pFontManager->MeasureChar2(unGid).fAdvanceY; } else { pFontManager->SetStringGID(FALSE); return pFontManager->MeasureChar2(unUnicode).fAdvanceY; } } Page::Page(const std::wstring& wsPagePath, IFolder* wsRootPath, CFontList* pFontList, NSFonts::IFontManager* pFontManager, CDocument* pDocument) { m_wsPagePath = wsPagePath; m_wsRootPath = wsRootPath; m_pFontList = pFontList; m_pFontManager = pFontManager; m_pDocument = pDocument; } Page::~Page() { } void Page::GetSize(int& nW, int& nH) const { XmlUtils::CXmlLiteReader oReader; if (!oReader.FromStringA(m_wsRootPath->readXml(m_wsPagePath))) return; if (!oReader.ReadNextNode()) return; CWString wsNodeName = oReader.GetNameNoNS(), wsAttrName; if (wsNodeName == L"AlternateContent") { if (!oReader.IsEmptyNode()) { int nAltDepth = oReader.GetDepth(); while (oReader.ReadNextSiblingNode(nAltDepth)) { wsNodeName = oReader.GetNameNoNS(); if (wsNodeName == L"Choice") { CWString wsAttr; ReadAttribute(oReader, L"Requires", wsAttr); if (wsAttr == L"xps") { if (!oReader.IsEmptyNode()) { int nAltDepth2 = oReader.GetDepth(); while (oReader.ReadNextSiblingNode(nAltDepth2)) { wsNodeName = oReader.GetNameNoNS(); if (wsNodeName == L"FixedPage") { ReadAttribute(oReader, L"Width", wsAttrName); nW = wsAttrName.tointeger(); ReadAttribute(oReader, L"Height", wsAttrName); nH = wsAttrName.tointeger(); break; } } } break; } } else if (wsNodeName == L"Fallback") { if (!oReader.IsEmptyNode()) { int nAltDepth2 = oReader.GetDepth(); while (oReader.ReadNextSiblingNode(nAltDepth2)) { wsNodeName = oReader.GetNameNoNS(); if (wsNodeName == L"FixedPage") { ReadAttribute(oReader, L"Width", wsAttrName); nW = wsAttrName.tointeger(); ReadAttribute(oReader, L"Height", wsAttrName); nH = wsAttrName.tointeger(); break; } } } break; } } } } else if (wsNodeName == L"FixedPage") { ReadAttribute(oReader, L"Width", wsAttrName); nW = wsAttrName.tointeger(); ReadAttribute(oReader, L"Height", wsAttrName); nH = wsAttrName.tointeger(); } } void Page::Draw(IRenderer* pRenderer, bool* pbBreak) { XmlUtils::CXmlLiteReader oReader; if (!oReader.FromStringA(m_wsRootPath->readXml(m_wsPagePath))) return; if (!oReader.ReadNextNode()) return; CContextState oState(pRenderer); CWString wsNodeName = oReader.GetNameNoNS(); if (wsNodeName == L"AlternateContent") { if (!oReader.IsEmptyNode()) { int nAltDepth = oReader.GetDepth(); while (oReader.ReadNextSiblingNode(nAltDepth)) { wsNodeName = oReader.GetNameNoNS(); if (wsNodeName == L"Choice") { CWString wsAttr; ReadAttribute(oReader, L"Requires", wsAttr); if (wsAttr == L"xps") { if (!oReader.IsEmptyNode()) { int nAltDepth2 = oReader.GetDepth(); while (oReader.ReadNextSiblingNode(nAltDepth2)) { wsNodeName = oReader.GetNameNoNS(); if (wsNodeName == L"FixedPage") { DrawCanvas(oReader, pRenderer, &oState, pbBreak); break; } } } break; } } else if (wsNodeName == L"Fallback") { if (!oReader.IsEmptyNode()) { int nAltDepth2 = oReader.GetDepth(); while (oReader.ReadNextSiblingNode(nAltDepth2)) { wsNodeName = oReader.GetNameNoNS(); if (wsNodeName == L"FixedPage") { DrawCanvas(oReader, pRenderer, &oState, pbBreak); break; } } } break; } } } } else if (wsNodeName == L"FixedPage") { DrawCanvas(oReader, pRenderer, &oState, pbBreak); } } void Page::DrawCanvas(XmlUtils::CXmlLiteReader& oReader, IRenderer* pRenderer, CContextState* pState, bool* pbBreak) { bool bTransform = false, bClip = false, bOpacity = false, bResource = false; if (oReader.MoveToFirstAttribute()) { std::string wsAttrName = oReader.GetNameA(); while (!wsAttrName.empty()) { if (wsAttrName == "Clip") bClip = ClipToRenderer(oReader.GetText().c_str(), pState); else if (wsAttrName == "RenderTransform") bTransform = TransformToRenderer(oReader.GetText().c_str(), pState); else if (wsAttrName == "Opacity") { pState->PushOpacity(GetDouble(oReader.GetText())); bOpacity = true; } if (!oReader.MoveToNextAttribute()) break; wsAttrName = oReader.GetNameA(); } } oReader.MoveToElement(); if (oReader.IsEmptyNode()) return; CWString wsNodeName; int nCurDepth = oReader.GetDepth(); while (oReader.ReadNextSiblingNode(nCurDepth)) { wsNodeName = oReader.GetNameNoNS(); if (wsNodeName == L"FixedPage.Resources") { bResource = ReadResource(oReader, pRenderer, pState); } else if (wsNodeName == L"Canvas.Resources") { bResource = ReadResource(oReader, pRenderer, pState); } else if (wsNodeName == L"Glyphs") { DrawGlyph(oReader, pRenderer, pState); } else if (wsNodeName == L"Canvas") { DrawCanvas(oReader, pRenderer, pState, pbBreak); } else if (wsNodeName == L"Canvas.RenderTransform" && !bTransform) { CWString wsTransform; ReadTransform(oReader, wsTransform); bTransform = TransformToRenderer(wsTransform.c_str(), pState); } else if (wsNodeName == L"Canvas.Clip" && !bClip) { CWString wsClip; ReadClip(oReader, wsClip); bClip = ClipToRenderer(wsClip.c_str(), pState); } else if (wsNodeName == L"Path") { DrawPath(oReader, pRenderer, pState); } else if (wsNodeName == L"AlternateContent") { if (!oReader.IsEmptyNode()) { int nAltDepth = oReader.GetDepth(); while (oReader.ReadNextSiblingNode(nAltDepth)) { wsNodeName = oReader.GetNameNoNS(); if (wsNodeName == L"Choice") { CWString wsAttr; ReadAttribute(oReader, L"Requires", wsAttr); if (wsAttr == L"xps") { DrawCanvas(oReader, pRenderer, pState, NULL); break; } } else if (wsNodeName == L"Fallback") { DrawCanvas(oReader, pRenderer, pState, NULL); break; } } } } if (NULL != pbBreak && *pbBreak) return; } if (bClip) pState->PopClip(); if (bTransform) pState->PopTransform(); if (bOpacity) pState->PopOpacity(); if (bResource) pState->PopResource(); } bool Page::ReadResource(XmlUtils::CXmlLiteReader& oReader, IRenderer* pRenderer, CContextState* pState) { if (oReader.IsEmptyNode()) return false; CWString wsNodeName; int nCurDepth = oReader.GetDepth(); while (oReader.ReadNextSiblingNode(nCurDepth)) { wsNodeName = oReader.GetNameNoNS(); if (wsNodeName == L"ResourceDictionary") { CWString wsSource; ReadAttribute(oReader, L"Source", wsSource); if (!wsSource.empty()) { std::wstring wsPath = wsSource.c_stdstr(); pState->PushResource(m_pDocument->GetStaticResource(wsPath.c_str()), false); } else { pState->PushResource(new CStaticResource(oReader), true); } return true; } } return false; } bool Page::ClipToRenderer(const wchar_t* wsString, CContextState* pState) { CWString wsClip; wsClip.create(wsString, true); if (!wsClip.empty()) { if (IsFromResource(wsClip)) { CWString wsPathTransform; pState->GetPathGeometry(wsClip, wsClip, wsPathTransform); } pState->PushClip(wsClip); return true; } return false; } bool Page::TransformToRenderer(const wchar_t* wsString, CContextState* pState) { CWString wsTransform = wsString; if (!wsTransform.empty()) { if (IsFromResource(wsTransform)) pState->GetTransform(wsTransform, wsTransform); std::vector arrElements = wsTransform.split(','); double arrRes[6] ={ 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 }; for (int nIndex = 0, nCount = std::min(6, (int)arrElements.size()); nIndex < nCount; nIndex++) arrRes[nIndex] = GetDouble(arrElements[nIndex]); pState->PushTransform(arrRes); return true; } return false; } void Page::DrawGlyph(XmlUtils::CXmlLiteReader& oReader, IRenderer* pRenderer, CContextState* pState) { double dFontSize = 10.0; bool bTransform = false, bClip = false, bOpacity = false; double dX = 0; double dY = 0; std::wstring wsFontPath; std::wstring wsIndicies; int nBidiLevel = 0; CWString wsClip, wsTransform; unsigned short* pUtf16 = NULL; unsigned short* pUtf16Ptr = NULL; unsigned int unUtf16Len = 0; CWString wsIndices; CWString wsFill; bool bIsSideways = false; bool bForceItalic = false; bool bForceBold = false; if (oReader.MoveToFirstAttribute()) { std::wstring wsAttrName = oReader.GetName(); while (!wsAttrName.empty()) { if (L"FontUri" == wsAttrName) { wsFontPath = oReader.GetText(); std::wstring wsFontName = GetFileName(wsFontPath); if (wsFontPath.length() >= 0 && '.' == wsFontPath.at(0)) { int nSlashPos = m_wsPagePath.find_last_of(L'/'); if (std::wstring::npos == nSlashPos) nSlashPos = -1; std::wstring wsRelativePath = (std::wstring::npos == nSlashPos) ? m_wsPagePath : m_wsPagePath.substr(0, nSlashPos + 1); wsFontPath = wsRelativePath + wsFontPath; } wsFontPath = m_wsRootPath->getFullFilePath(wsFontPath); std::wstring wsExt = GetFileExtension(wsFontPath); NSStringExt::ToLower(wsExt); if (L"odttf" == wsExt) { NSStringExt::ToLower(wsFontName); m_pFontList->Check(wsFontName, wsFontPath, m_wsRootPath); } else { // шрифт не odttf - надо добавить его во внешний сторадж шрифтов, если нужно if (IFolder::iftZip == m_wsRootPath->getType() && NSFonts::NSApplicationFontStream::GetGlobalMemoryStorage()) { IFolder::CBuffer* buffer = NULL; m_wsRootPath->read(wsFontPath, buffer); if (NSFonts::NSApplicationFontStream::GetGlobalMemoryStorage()) NSFonts::NSApplicationFontStream::GetGlobalMemoryStorage()->Add(wsFontPath, buffer->Buffer, buffer->Size); RELEASEOBJECT(buffer); } } wsFontPath = NormalizePath(wsFontPath); pRenderer->put_FontPath(wsFontPath); } else if (wsAttrName == L"Opacity") { double dOpacity; ReadSTDouble(oReader.GetText(), dOpacity); pState->PushOpacity(dOpacity); bOpacity = true; } else if (L"Clip" == wsAttrName) { wsClip.create(oReader.GetText(), true); } else if (L"Fill" == wsAttrName) { wsFill.create(oReader.GetText(), true); } else if (L"StyleSimulations" == wsAttrName) { CWString wsFontStyle = oReader.GetText(); if (wsFontStyle == L"ItalicSimulation") { bForceItalic = true; } else if (wsFontStyle == L"BoldSimulation") { bForceBold = true; } else if (wsFontStyle == L"BoldItalicSimulation") { bForceItalic = true; bForceBold = true; } } else if (L"FontRenderingEmSize" == wsAttrName) { dFontSize = GetDouble(oReader.GetText()); } else if (L"RenderTransform" == wsAttrName) { wsTransform.create(oReader.GetText(), true); } else if (L"UnicodeString" == wsAttrName) { CWString wsUnicodeString = oReader.GetText(); if (!wsUnicodeString.empty()) { pUtf16Ptr = NSStringExt::CConverter::GetUtf16FromUnicode(wsUnicodeString.c_str(), unUtf16Len); if (unUtf16Len >= 2 && '{' == pUtf16Ptr[0] && '}' == pUtf16Ptr[1]) { pUtf16 = pUtf16Ptr + 2; unUtf16Len -= 2; } else { pUtf16 = pUtf16Ptr; } } } else if (L"OriginX" == wsAttrName) { dX = GetDouble(oReader.GetText()); } else if (L"OriginY" == wsAttrName) { dY = GetDouble(oReader.GetText()); } else if (L"Indices" == wsAttrName) { wsIndices.create(oReader.GetText(), true); wsIndicies = oReader.GetText(); } else if (L"BidiLevel" == wsAttrName) { nBidiLevel = GetInteger(oReader.GetText()); } else if (wsAttrName == L"IsSideways") { bIsSideways = GetBool(oReader.GetText()); } if (!oReader.MoveToNextAttribute()) break; wsAttrName = oReader.GetName(); } } oReader.MoveToElement(); CBrush* pBrush = NULL; bool bDeleteBrush = false; if (!wsFill.empty()) { if (IsFromResource(wsFill)) { pBrush = pState->GetBrush(wsFill); bDeleteBrush = false; } else { pBrush = ReadBrush(wsFill.c_str(), pState->GetCurrentOpacity()); bDeleteBrush = true; } } if (!oReader.IsEmptyNode()) { CWString wsNodeName; int nCurDepth = oReader.GetDepth(); while (oReader.ReadNextSiblingNode(nCurDepth)) { wsNodeName = oReader.GetNameNoNS(); if (wsNodeName == L"Glyphs.RenderTransform") { ReadTransform(oReader, wsTransform); } else if (wsNodeName == L"Glyphs.Fill" && !pBrush) { pBrush = ReadBrush(oReader, pState->GetCurrentOpacity()); bDeleteBrush = true; } } } if (!pBrush || !pBrush->SetToRenderer(pRenderer)) { if (bDeleteBrush) RELEASEOBJECT(pBrush); RELEASEARRAYOBJECTS(pUtf16Ptr); if (bClip) pState->PopClip(); if (bTransform) pState->PopTransform(); if (bOpacity) pState->PopOpacity(); return; } // Сначала задается матрица преобразования, потом клип, потому что даже // если преобразование задано в дочерней ноде, а клип задан в атрибутах данной ноды, // то преобразование влияется на клип все равно. if (!wsTransform.empty()) { bTransform = TransformToRenderer(wsTransform.c_str(), pState); if (dFontSize < 5) { double dDet = pState->NormalizeTransform(); dFontSize *= dDet; } } if (!wsClip.empty()) { bClip = ClipToRenderer(wsClip.c_str(), pState); } pRenderer->put_FontSize(dFontSize * 0.75); TIndicesEntry oEntry; int nIndicesPos = 0, nIndicesLen = wsIndices.size(); int nUtf16Pos = 0; bool bRtoL = (nBidiLevel % 2 ? true : false); std::wstring sFullFontPath = m_wsRootPath->getFullFilePath(wsFontPath); if (NSFonts::NSApplicationFontStream::GetGlobalMemoryStorage()) { NSFonts::IFontStream* pMemoryStream = NSFonts::NSApplicationFontStream::GetGlobalMemoryStorage()->Get(sFullFontPath); if (!pMemoryStream) sFullFontPath = L""; } if (!sFullFontPath.empty()) m_pFontManager->LoadFontFromFile(sFullFontPath, 0, (float)(dFontSize * 0.75), 96, 96); double dFontKoef = dFontSize / 100.0; bool bNeedItalic = false, bNeedBold = false, bChangeFont = true; NSFonts::IFontFile* pFile = m_pFontManager->GetFile(); if (pFile) { if (!pFile->IsItalic() && bForceItalic) bNeedItalic = true; if (!pFile->IsBold() && bForceBold) { LONG lTextColor, lTextAlpha; pRenderer->get_BrushColor1(&lTextColor); pRenderer->get_BrushAlpha1(&lTextAlpha); pRenderer->put_PenColor(lTextColor); pRenderer->put_PenAlpha(lTextAlpha); pRenderer->put_PenSize(xpsUnitToMM(1)); bNeedBold = true; } } if (!bIsSideways) { while (GetNextGlyph(wsIndices.c_str(), nIndicesPos, nIndicesLen, pUtf16, nUtf16Pos, unUtf16Len, oEntry)) { double dAdvance, dRealAdvance; if (oEntry.bAdvance) { dAdvance = oEntry.dAdvance * dFontKoef; if (bRtoL) dRealAdvance = GetAdvanceX(m_pFontManager, oEntry.nUnicode, oEntry.nGid, oEntry.bGid); else dRealAdvance = dAdvance; } else { dAdvance = GetAdvanceX(m_pFontManager, oEntry.nUnicode, oEntry.nGid, oEntry.bGid); dRealAdvance = dAdvance; } if (bRtoL) dX -= dRealAdvance; double dXorigin = (oEntry.bHorOffset || oEntry.bVerOffset) ? dX + (bRtoL ? -oEntry.dHorOffset * dFontKoef : oEntry.dHorOffset * dFontKoef) : dX; double dYorigin = (oEntry.bHorOffset || oEntry.bVerOffset) ? dY - oEntry.dVerOffset * dFontKoef : dY; if (bNeedItalic) { double dAlpha = sin(-15 * M_PI / 180); double pTransform[] ={ 1, 0, dAlpha, 1, -dAlpha * dYorigin, 0 }; pState->PushTransform(pTransform); } if (oEntry.bGid) { pRenderer->CommandDrawTextExCHAR(oEntry.nUnicode, oEntry.nGid, xpsUnitToMM(dXorigin), xpsUnitToMM(dYorigin), 0, 0); } else { LONG nRenType = 0; pRenderer->get_Type(&nRenType); if (c_nGrRenderer == nRenType) pRenderer->put_FontStringGID(FALSE); pRenderer->CommandDrawTextCHAR(oEntry.nUnicode, xpsUnitToMM(dXorigin), xpsUnitToMM(dYorigin), 0, 0); } if (bNeedBold) { pRenderer->BeginCommand(c_nPathType); pRenderer->PathCommandStart(); if (oEntry.bGid) pRenderer->PathCommandTextExCHAR(oEntry.nUnicode, oEntry.nGid, xpsUnitToMM(dXorigin), xpsUnitToMM(dYorigin), 0, 0); else pRenderer->PathCommandTextCHAR(oEntry.nUnicode, xpsUnitToMM(dXorigin), xpsUnitToMM(dYorigin), 0, 0); pRenderer->DrawPath(c_nStroke); pRenderer->EndCommand(c_nPathType); pRenderer->PathCommandEnd(); } if (bNeedItalic) pState->PopTransform(); if (!bRtoL) dX += dAdvance; else dX -= (dAdvance - dRealAdvance); } } else { while (GetNextGlyph(wsIndices.c_str(), nIndicesPos, nIndicesLen, pUtf16, nUtf16Pos, unUtf16Len, oEntry)) { double dAdvanceX = GetAdvanceX(m_pFontManager, oEntry.nUnicode, oEntry.nGid, oEntry.bGid); double dAdvanceY = GetAdvanceY(m_pFontManager, oEntry.nUnicode, oEntry.nGid, oEntry.bGid); double dAdvance; if (oEntry.bAdvance) dAdvance = oEntry.dAdvance * dFontKoef; else dAdvance = dAdvanceY; double dXorigin = (oEntry.bHorOffset || oEntry.bVerOffset) ? dX + oEntry.dHorOffset * dFontKoef : dX; double dYorigin = (oEntry.bHorOffset || oEntry.bVerOffset) ? dY - oEntry.dVerOffset * dFontKoef : dY; if (bNeedItalic) { double dAlpha = sin(15 * M_PI / 180); double pTransform[] ={ 1, dAlpha, 0, 1, 0, -dAlpha * dXorigin }; pState->PushTransform(pTransform); } double pTransform[] ={ 0, -1, 1, 0, dXorigin + dAdvanceY, dYorigin + dAdvanceX / 2 }; pState->PushTransform(pTransform); if (oEntry.bGid) { pRenderer->CommandDrawTextExCHAR(oEntry.nUnicode, oEntry.nGid, 0, 0, 0, 0); } else { pRenderer->CommandDrawTextCHAR(oEntry.nUnicode, 0, 0, 0, 0); } if (bNeedBold) { pRenderer->BeginCommand(c_nPathType); pRenderer->PathCommandStart(); if (oEntry.bGid) pRenderer->PathCommandTextExCHAR(oEntry.nUnicode, oEntry.nGid, 0, 0, 0, 0); else pRenderer->PathCommandTextCHAR(oEntry.nUnicode, 0, 0, 0, 0); pRenderer->DrawPath(c_nStroke); pRenderer->EndCommand(c_nPathType); pRenderer->PathCommandEnd(); } pState->PopTransform(); if (bNeedItalic) pState->PopTransform(); dX += dAdvance; } } if (bDeleteBrush) RELEASEOBJECT(pBrush); RELEASEARRAYOBJECTS(pUtf16Ptr); if (bClip) pState->PopClip(); if (bTransform) pState->PopTransform(); if (bOpacity) pState->PopOpacity(); } void Page::DrawPath(XmlUtils::CXmlLiteReader& oReader, IRenderer* pRenderer, CContextState* pState) { bool bTransform = false, bClip = false, bOpacity = false; double dPenSize = 1.0; bool bStroke = false; bool bFill = false; int nFillBgr = 0, nFillAlpha = 255, nStrokeBgr = 0, nStrokeAlpha = 255; BYTE nDashCap = Aggplus::LineCapFlat; BYTE nStartCap = Aggplus::LineCapFlat; BYTE nEndCap = Aggplus::LineCapFlat; BYTE nJoinStyle = Aggplus::LineJoinMiter; double dMiter = 10.0; double* pDashPattern = NULL; LONG lDashPatternSize = 0; double dDashOffset = 0.0; CWString wsFill; CWString wsClip, wsTransform, wsPathData, wsPathTransform; std::vector::iterator find = m_pDocument->m_vStructure.end(); if (oReader.MoveToFirstAttribute()) { std::wstring wsAttrName = oReader.GetName(); while (!wsAttrName.empty()) { if (L"RenderTransform" == wsAttrName) { wsTransform.create(oReader.GetText(), true); } else if (L"Clip" == wsAttrName) { wsClip.create(oReader.GetText(), true); } else if (L"Opacity" == wsAttrName) { double dOpacity; ReadSTDouble(oReader.GetText(), dOpacity); pState->PushOpacity(dOpacity); bOpacity = true; } else if (L"Stroke" == wsAttrName) { std::wstring wsStrokeColor = oReader.GetText(); GetBgra(wsStrokeColor, nStrokeBgr, nStrokeAlpha); bStroke = true; } else if (L"StrokeThickness" == wsAttrName) { std::wstring wsPenSize = oReader.GetText(); dPenSize = GetDouble(wsPenSize); } else if (L"StrokeDashArray" == wsAttrName) { std::wstring wsDashArray = oReader.GetText(); std::vector arrDashArray = NSStringExt::Split(wsDashArray, ' '); int nDashArrayCount = arrDashArray.size(); if (nDashArrayCount > 0) { pDashPattern = new double[nDashArrayCount]; if (pDashPattern) { lDashPatternSize = nDashArrayCount; for (int nIndex = 0; nIndex < nDashArrayCount; nIndex++) { pDashPattern[nIndex] = GetDouble(arrDashArray.at(nIndex)); } } } } else if (L"StrokeDashOffset" == wsAttrName) { std::wstring wsDashOffset = oReader.GetText(); dDashOffset = GetDouble(wsDashOffset); } else if (L"StrokeDashCap" == wsAttrName) { nDashCap = GetCapStyle(oReader.GetTextA()); } else if (L"StrokeEndLineCap" == wsAttrName) { nEndCap = GetCapStyle(oReader.GetTextA()); } else if (L"StrokeStartLineCap" == wsAttrName) { nStartCap = GetCapStyle(oReader.GetTextA()); } else if (L"StrokeLineJoin" == wsAttrName) { CWString wsJoin = oReader.GetText(); if (wsJoin == L"Miter") nJoinStyle = Aggplus::LineJoinMiter; else if (wsJoin == L"Bevel") nJoinStyle = Aggplus::LineJoinBevel; else if (wsJoin == L"Round") nJoinStyle = Aggplus::LineJoinRound; } else if (L"StrokeMiterLimit" == wsAttrName) { std::wstring wsMiterLimit = oReader.GetText(); dMiter = GetDouble(wsMiterLimit); } else if (L"Fill" == wsAttrName) { wsFill.create(oReader.GetText(), true); } else if (L"Data" == wsAttrName) { wsPathData.create(oReader.GetText(), true); } else if (L"Name" == wsAttrName) { std::wstring wsNameTarget = oReader.GetText(); find = std::find_if(m_pDocument->m_vStructure.begin(), m_pDocument->m_vStructure.end(), [wsNameTarget](const CDocument::CDocumentStructure& str){ return str.wsTarget == wsNameTarget; }); } else if (L"FixedPage.NavigateUri" == wsAttrName) { double pdA, pdB, pdC, pdD, pdE, pdF; pRenderer->GetTransform(&pdA, &pdB, &pdC, &pdD, &pdE, &pdF); Aggplus::CMatrix oTransform(pdA, pdB, pdC, pdD, pdE, pdF); double x1 = 0, y1 = 0, x2 = 0, y2 = 0, x3 = 0, y3 = 0; NSWasm::CPageLinkItem oLink = {"", 0, 0, 0, 0, 0}; std::wstring wsPath = wsPathData.c_stdstr(); size_t nFindX = wsPath.find(L"M "); if (nFindX != std::wstring::npos) { nFindX += 2; size_t nFindEndX = wsPath.find(L',', nFindX); if (nFindEndX != std::wstring::npos) { x1 = GetDouble(wsPath.substr(nFindX, nFindEndX - nFindX)); size_t nFindY = nFindEndX + 1; size_t nFindEndY = wsPath.find(L' ', nFindY); if (nFindEndY != std::wstring::npos) y1 = GetDouble(wsPath.substr(nFindY, nFindEndY - nFindY)); oTransform.TransformPoint(x1, y1); } } nFindX = wsPath.find(L"L "); if (nFindX != std::wstring::npos) { nFindX += 2; size_t nFindEndX = wsPath.find(L',', nFindX); if (nFindEndX != std::wstring::npos) { x2 = GetDouble(wsPath.substr(nFindX, nFindEndX - nFindX)); size_t nFindY = nFindEndX + 1; size_t nFindEndY = wsPath.find(L' ', nFindY); if (nFindEndY != std::wstring::npos) y2 = GetDouble(wsPath.substr(nFindY, nFindEndY - nFindY)); oTransform.TransformPoint(x2, y2); } } nFindX = wsPath.find(L"L ", nFindX); if (nFindX != std::wstring::npos) { nFindX += 2; size_t nFindEndX = wsPath.find(L',', nFindX); if (nFindEndX != std::wstring::npos) { x3 = GetDouble(wsPath.substr(nFindX, nFindEndX - nFindX)); size_t nFindY = nFindEndX + 1; size_t nFindEndY = wsPath.find(L' ', nFindY); if (nFindEndY != std::wstring::npos) y3 = GetDouble(wsPath.substr(nFindY, nFindEndY - nFindY)); oTransform.TransformPoint(x3, y3); } } // Верхний левый угол oLink.X = x1 == x2 ? fmin(x1, x3) : fmin(x1, x2); oLink.Y = y1 == y2 ? fmin(y1, y3) : fmin(y1, y2); oLink.H = x1 == x2 ? abs(y1 - y2) : abs(y1 - y3); oLink.W = y1 == y2 ? abs(x1 - x2) : abs(x1 - x3); std::wstring wsNameTarget = oReader.GetText(); if (wsNameTarget.find(L"http") == 0) { oLink.Link = U_TO_UTF8(wsNameTarget); m_oLinks.m_arLinks.push_back(oLink); } else { // координата назначения на странице назначения size_t nSharp = wsNameTarget.find(L'#'); if (nSharp != std::wstring::npos) { std::map::iterator find = m_pDocument->m_mInternalLinks.find(wsNameTarget.substr(nSharp + 1)); if (find != m_pDocument->m_mInternalLinks.end()) { oLink.Link = '#' + std::to_string(find->second); m_oLinks.m_arLinks.push_back(oLink); } } } } if (!oReader.MoveToNextAttribute()) break; wsAttrName = oReader.GetName(); } } oReader.MoveToElement(); if (find != m_pDocument->m_vStructure.end()) { std::wstring wsPath = wsPathData.c_stdstr(); size_t nFindY = wsPath.find(L','); if (nFindY != std::wstring::npos) { size_t nFindEndY = wsPath.find(L' ', ++nFindY); if (nFindEndY != std::wstring::npos) // координата назначения на странице назначения find->dY = GetDouble(wsPath.substr(nFindY, nFindEndY - nFindY)); } } CBrush* pBrush = NULL; bool bDeleteBrush = false; if (!wsFill.empty()) { if (IsFromResource(wsFill)) { pBrush = pState->GetBrush(wsFill); bDeleteBrush = false; } else { pBrush = ReadBrush(wsFill.c_str(), pState->GetCurrentOpacity()); bDeleteBrush = true; } } if (bStroke) { pRenderer->put_PenColor(nStrokeBgr & 0x00FFFFFF); pRenderer->put_PenAlpha(nStrokeAlpha * pState->GetCurrentOpacity()); } if (!oReader.IsEmptyNode()) { CWString wsNodeName; int nCurDepth = oReader.GetDepth(); while (oReader.ReadNextSiblingNode(nCurDepth)) { wsNodeName = oReader.GetNameNoNS(); if (wsNodeName == L"Path.RenderTransform") { ReadTransform(oReader, wsTransform); } else if (wsNodeName == L"Path.Clip") { ReadClip(oReader, wsClip); } else if (wsNodeName == L"Path.Fill" && !pBrush) { pBrush = ReadBrush(oReader, pState->GetCurrentOpacity()); bDeleteBrush = true; } else if (wsNodeName == L"Path.Stroke" && !bStroke) { bStroke = StrokeToRenderer(oReader, pRenderer, pState); } else if (wsNodeName == L"Path.Data" && wsPathData.empty()) { ReadPathData(oReader, wsPathData, wsPathTransform); } } } if (pBrush) { if (pBrush->IsImageBrush()) ((CImageBrush*)pBrush)->SetPaths(m_wsRootPath, GetPath(m_wsPagePath).c_str()); bFill = pBrush->SetToRenderer(pRenderer); if (bDeleteBrush) RELEASEOBJECT(pBrush); } // Сначала задается матрица преобразования, потом клип, потому что даже // если преобразование задано в дочерней ноде, а клип задан в атрибутах данной ноды, // то преобразование влияется на клип все равно. if (!wsTransform.empty()) { bTransform = TransformToRenderer(wsTransform.c_str(), pState); } if (!wsClip.empty()) { bClip = ClipToRenderer(wsClip.c_str(), pState); } if (pDashPattern) { for (LONG lIndex = 0; lIndex < lDashPatternSize; lIndex++) { pDashPattern[lIndex] = xpsUnitToMM(pDashPattern[lIndex] * dPenSize); } pRenderer->put_PenDashStyle(Aggplus::DashStyleCustom); pRenderer->PenDashPattern(pDashPattern, lDashPatternSize); pRenderer->put_PenDashOffset(xpsUnitToMM(dDashOffset * dPenSize)); pRenderer->put_PenLineStartCap(nDashCap); pRenderer->put_PenLineEndCap(nDashCap); delete[] pDashPattern; } else { pRenderer->put_PenDashStyle(Aggplus::DashStyleSolid); pRenderer->put_PenLineStartCap(nStartCap); pRenderer->put_PenLineEndCap(nEndCap); } pRenderer->put_PenLineJoin(nJoinStyle); if (nJoinStyle == Aggplus::LineJoinMiter) pRenderer->put_PenMiterLimit(xpsUnitToMM(dMiter)); pRenderer->put_PenSize(xpsUnitToMM(dPenSize)); pRenderer->BeginCommand(c_nPathType); pRenderer->PathCommandStart(); if (IsFromResource(wsPathData)) pState->GetPathGeometry(wsPathData, wsPathData, wsPathTransform); bool bPathTransform = false; if (!wsPathTransform.empty()) bPathTransform = TransformToRenderer(wsPathTransform.c_str(), pState); bool bWindingFillMode = VmlToRenderer(wsPathData, pRenderer); int nMode = bStroke ? c_nStroke : 0; if (bFill) nMode |= (bWindingFillMode ? c_nWindingFillMode : c_nEvenOddFillMode); pRenderer->DrawPath(nMode); pRenderer->EndCommand(c_nPathType); pRenderer->PathCommandEnd(); if (bPathTransform) pState->PopTransform(); if (bTransform) pState->PopTransform(); if (bClip) pState->PopClip(); if (bOpacity) pState->PopOpacity(); } bool Page::StrokeToRenderer(XmlUtils::CXmlLiteReader& oReader, IRenderer* pRenderer, CContextState* pState) { if (!oReader.IsEmptyNode()) { std::wstring wsNodeName; int nCurDepth = oReader.GetDepth(); while (oReader.ReadNextSiblingNode(nCurDepth)) { wsNodeName = oReader.GetNameNoNS(); if (L"SolidColorBrush" == wsNodeName) { int nBgr, nAlpha; std::wstring wsColor; ReadAttribute(oReader, L"Color", wsColor); GetBgra(wsColor, nBgr, nAlpha); pRenderer->put_PenColor(nBgr & 0x00FFFFFF); pRenderer->put_PenAlpha((double)nAlpha * pState->GetCurrentOpacity()); return true; } } } return false; } void Page::ReadPathData(XmlUtils::CXmlLiteReader& oReader, CWString& wsData, CWString& wsTransform) { wsData = L""; if (oReader.IsEmptyNode()) return; CWString wsNodeName; int nCurDepth = oReader.GetDepth(); while (oReader.ReadNextSiblingNode(nCurDepth)) { wsNodeName = oReader.GetNameNoNS(); if (wsNodeName == L"PathGeometry") return ReadPathGeometry(oReader, wsData, wsTransform); } } }