/* * (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 "States.h" #include "Font.h" #include "Types.h" #include "Pages.h" #include "FontCidTT.h" #include "../PdfWriter.h" #include "Streams.h" #include "Utils.h" //---------------------------------------------------------------------------------------- // // CCommandManager // //---------------------------------------------------------------------------------------- CCommandManager::CCommandManager(CPdfWriter* pRenderer) : m_pRenderer(pRenderer) { } CCommandManager::~CCommandManager() { Clear(); } CRendererTextCommand* CCommandManager::AddText(unsigned char* pCodes, unsigned int nLen, const double& dX, const double& dY) { CRendererCommandBase* pCommand = new CRendererTextCommand(pCodes, nLen, dX, dY); Add(pCommand); return (CRendererTextCommand*)pCommand; } void CCommandManager::Add(CRendererCommandBase* pCommand) { if (pCommand) { if (m_vCommands.size() > 0 && pCommand->GetType() != m_vCommands.at(0)->GetType()) Flush(); m_vCommands.push_back(pCommand); } } void CCommandManager::Flush() { size_t nCommandsCount = m_vCommands.size(); if (nCommandsCount > 0) { PdfWriter::CPage* pPage = m_pRenderer->GetPage(); pPage->GrSave(); pPage->SetTransform(m_oTransform.m11, m_oTransform.m12, m_oTransform.m21, m_oTransform.m22, m_oTransform.dx, m_oTransform.dy); ERendererCommandType eType = m_vCommands.at(0)->GetType(); if (renderercommandtype_Text == eType) { pPage->BeginText(); CRendererTextCommand* pText = NULL; PdfWriter::CFontDict* pTextFont = NULL; double dTextSize = -1; LONG lTextColor = 0; BYTE nTextAlpha = 255; double dTextSpace = 0; double dHorScaling = 100; PdfWriter::ETextRenderingMode eMode = PdfWriter::textrenderingmode_Fill; bool isNeedDoBold = false; bool isNeedDoItalic = false; double dLineWidth = -1; double dRise = 0; double dWordSpace = 0; double dColor[4] = { 0, 0, 0, 0 }; int nColorSize = 1; std::wstring sTextName; PdfWriter::CTextLine oTextLine; for (size_t nIndex = 0; nIndex < nCommandsCount; nIndex++) { pText = (CRendererTextCommand*)m_vCommands.at(nIndex); if (!pText) continue; if (pTextFont != pText->GetFont() || fabs(dTextSize - pText->GetSize()) > 0.001) { oTextLine.Flush(pPage); pTextFont = pText->GetFont(); dTextSize = pText->GetSize(); if (pTextFont) pPage->SetFontAndSize(pTextFont, dTextSize); } if (sTextName != pText->GetName() || fabs(dTextSize - pText->GetSize()) > 0.001) { oTextLine.Flush(pPage); sTextName = pText->GetName(); dTextSize = pText->GetSize(); if (!sTextName.empty()) { std::string sKey = U_TO_UTF8(sTextName); pPage->SetFontKeyAndSize(sKey.c_str(), dTextSize); pPage->SetFontType(pText->GetFontType()); } } if (lTextColor != pText->GetColor()) { oTextLine.Flush(pPage); lTextColor = pText->GetColor(); TColor oColor = lTextColor; pPage->SetFillColor(oColor.r, oColor.g, oColor.b); pPage->SetStrokeColor(oColor.r, oColor.g, oColor.b); } if (nTextAlpha != pText->GetAlpha()) { oTextLine.Flush(pPage); nTextAlpha = pText->GetAlpha(); pPage->SetFillAlpha(nTextAlpha); pPage->SetStrokeAlpha(nTextAlpha); } if (fabs(dTextSpace - pText->GetSpace()) > 0.001) { oTextLine.Flush(pPage); dTextSpace = pText->GetSpace(); pPage->SetCharSpace(dTextSpace); } if ((int)eMode != pText->GetMode() || isNeedDoBold != pText->IsNeedDoBold()) { oTextLine.Flush(pPage); eMode = (PdfWriter::ETextRenderingMode)pText->GetMode(); isNeedDoBold = pText->IsNeedDoBold(); if (isNeedDoBold && eMode == PdfWriter::textrenderingmode_Fill) { double dNewLineWidth = dTextSize / 12 * 0.343; if (fabs(dLineWidth - dNewLineWidth) > 0.001) { dLineWidth = dNewLineWidth; pPage->SetLineWidth(dLineWidth); } pPage->SetTextRenderingMode(PdfWriter::textrenderingmode_FillThenStroke); } else { pPage->SetTextRenderingMode(eMode); } } if (fabs(dHorScaling - pText->GetHorScaling()) > 0.001) { oTextLine.Flush(pPage); dHorScaling = pText->GetHorScaling(); pPage->SetHorizontalScaling(dHorScaling); } if (isNeedDoItalic != pText->IsNeedDoItalic()) { oTextLine.Flush(pPage); if (pText->IsNeedDoItalic()) pPage->SetTextMatrix(1, 0, 0.26, 1, 0, 0); else pPage->SetTextMatrix(1, 0, 0, 1, 0, 0); isNeedDoItalic = pText->IsNeedDoItalic(); } if (fabs(dRise - pText->GetRise()) > 0.001) { oTextLine.Flush(pPage); dRise = pText->GetRise(); pPage->SetTextRise(dRise); } if (fabs(dWordSpace - pText->GetWordSpace()) > 0.001) { oTextLine.Flush(pPage); dWordSpace = pText->GetWordSpace(); pPage->SetWordSpace(dWordSpace); } if (!pText->GetPUA().empty()) { oTextLine.Flush(pPage); PdfWriter::CDictObject* pBDC = new PdfWriter::CDictObject(); pBDC->Add("ActualText", new PdfWriter::CStringObject(pText->GetPUA().c_str(), true)); pPage->BeginMarkedContentDict("Span", pBDC); RELEASEOBJECT(pBDC); } unsigned char* pCodes = pText->GetCodes(); unsigned short ushCode = (pCodes[0] << 8) + pCodes[1]; unsigned int unLen = pText->GetCodesLen(); double dX = pText->GetX(); double dY = pText->GetY(); double dTextSize = pText->GetSize(); double dWidth = pText->GetFont() ? (pText->GetFont()->GetWidth(ushCode) / 1000.0 * dTextSize) : pText->GetWidth(); if (!oTextLine.Add(pCodes, unLen, dX, dY, dWidth, dTextSize)) { oTextLine.Flush(pPage); if (!oTextLine.Add(pCodes, unLen, dX, dY, dWidth, dTextSize)) { pPage->DrawText(dX, dY, pCodes, unLen); } } if (!pText->GetPUA().empty()) { oTextLine.Flush(pPage); pPage->EndMarkedContent(); } } oTextLine.Flush(pPage); pPage->EndText(); } pPage->GrRestore(); } Clear(); } void CCommandManager::Clear() { for (size_t nIndex = 0, nCount = m_vCommands.size(); nIndex < nCount; nIndex++) { CRendererCommandBase* pCommand = m_vCommands.at(nIndex); delete pCommand; } m_vCommands.clear(); } //---------------------------------------------------------------------------------------- // Внутренние функции //---------------------------------------------------------------------------------------- static inline void UpdateMaxMinPoints(double& dMinX, double& dMinY, double& dMaxX, double& dMaxY, const double& dX, const double& dY) { if (dX < dMinX) dMinX = dX; if (dX > dMaxX) dMaxX = dX; if (dY < dMinY) dMinY = dY; if (dY > dMaxY) dMaxY = dY; } bool SkipPath(const std::vector& arrForStroke, const PdfWriter::CPoint& P1, const PdfWriter::CPoint& P2) { for (int i = 0; i < arrForStroke.size(); ++i) { PdfWriter::CPoint P3 = arrForStroke[i].start; PdfWriter::CPoint P4 = arrForStroke[i].end; // Вычисляем коэффициенты A, B, C для уравнения прямой P3P4: Ax + By + C = 0 double A = P4.y - P3.y; double B = P3.x - P4.x; double C = P4.x * P3.y - P3.x * P4.y; // Проверяем, лежит ли точка P1 на прямой P3P4 double check1 = A * P1.x + B * P1.y + C; // Проверяем, лежит ли точка P2 на прямой P3P4 double check2 = A * P2.x + B * P2.y + C; // Если обе проверки близки к нулю (в пределах эпсилон), то лежит if ((std::abs(check1) < 0.006) && (std::abs(check2) < 0.006)) return true; } return false; } bool CPath::DrawPathRedact(PdfWriter::CMatrix* pMatrix, Aggplus::CGraphicsPath* oPath, bool bStroke, const std::vector& arrForStroke) { PdfWriter::CMatrix oInverse = pMatrix->Inverse(); size_t length = oPath->GetPointCount(), compound = oPath->GetCloseCount(); std::vector points = oPath->GetPoints(0, length + compound); if (length + compound == 0) return false; double dXStart = -1, dYStart = -1, dXCur = -1, dYCur = -1; bool bBreak = false; for (size_t i = 0; i < length + compound; i++) { if (oPath->IsCurvePoint(i)) { double dX = points[i].X; double dY = points[i].Y; oInverse.Apply(dX, dY); double dX2 = points[i + 1].X; double dY2 = points[i + 1].Y; oInverse.Apply(dX2, dY2); double dX3 = points[i + 2].X; double dY3 = points[i + 2].Y; if (bBreak) { bBreak = false; double dXCI = dXCur, dYCI = dYCur; oInverse.Apply(dXCI, dYCI); MoveTo(dXCI, dYCI); } dXCur = dX3; dYCur = dY3; oInverse.Apply(dX3, dY3); CurveTo(dX, dY, dX2, dY2, dX3, dY3); i += 2; } else if (oPath->IsMovePoint(i)) { double dX = points[i].X, dY = points[i].Y; dXStart = dX; dYStart = dY; dXCur = dX; dYCur = dY; if (bStroke) bBreak = true; else { oInverse.Apply(dX, dY); MoveTo(dX, dY); } } else if (oPath->IsLinePoint(i)) { double dX = points[i].X, dY = points[i].Y; if (bStroke && SkipPath(arrForStroke, PdfWriter::CPoint(dXCur, dYCur), PdfWriter::CPoint(dX, dY))) { dXCur = dX; dYCur = dY; bBreak = true; continue; } if (bBreak) { bBreak = false; double dXCI = dXCur, dYCI = dYCur; oInverse.Apply(dXCI, dYCI); MoveTo(dXCI, dYCI); } dXCur = dX; dYCur = dY; oInverse.Apply(dX, dY); LineTo(dX, dY); } else if (oPath->IsClosePoint(i)) { if (bStroke && (std::abs(dXCur - dXStart) > PdfWriter::EPS || std::abs(dYCur - dYStart) > PdfWriter::EPS) && SkipPath(arrForStroke, PdfWriter::CPoint(dXCur, dYCur), PdfWriter::CPoint(dXStart, dYStart))) { dXCur = dXStart; dYCur = dYStart; bBreak = true; continue; } if (bStroke || bBreak) { if (std::abs(dXCur - dXStart) > PdfWriter::EPS || std::abs(dYCur - dYStart) > PdfWriter::EPS) { bBreak = false; double dXCI = dXCur, dYCI = dYCur; oInverse.Apply(dXCI, dYCI); double dXSI = dXStart, dYSI = dYStart; oInverse.Apply(dXSI, dYSI); MoveTo(dXCI, dYCI); LineTo(dXSI, dYSI); } } else Close(); } } return true; } void CPath::Redact(PdfWriter::CMatrix* pMatrix, const std::vector& arrRedact, PdfWriter::CPage* pPage, bool bStroke, bool bFill, bool bEoFill, PdfWriter::CShading* pShading, PdfWriter::CExtGrState* pShadingExtGrState) { PdfWriter::CMatrix oInverse = pMatrix->Inverse(); Aggplus::CGraphicsPath oPath, oPathRedact, oPathResult; for (int i = 0; i < arrRedact.size(); i += 8) { oPathRedact.StartFigure(); oPathRedact.MoveTo(arrRedact[i + 0], arrRedact[i + 1]); oPathRedact.LineTo(arrRedact[i + 2], arrRedact[i + 3]); oPathRedact.LineTo(arrRedact[i + 4], arrRedact[i + 5]); oPathRedact.LineTo(arrRedact[i + 6], arrRedact[i + 7]); oPathRedact.CloseFigure(); } std::vector vCommands = m_vCommands; for (int nIndex = 0, nCount = vCommands.size(); nIndex < nCount; nIndex++) { CPathCommandBase* pCommand = vCommands.at(nIndex); // CPath to CGraphicsPath pCommand->ToCGraphicsPath(pMatrix, oPath); } if (bEoFill) oPath.SetRuler(true); m_vCommands.clear(); if (bFill || bEoFill) { oPathResult = Aggplus::CalcBooleanOperation(oPath, oPathRedact, Aggplus::BooleanOpType::Subtraction); bool bPath = DrawPathRedact(pMatrix, &oPathResult, bStroke); if (bPath) { if (!pShading) Draw(pPage, false, bFill, bEoFill); else { pPage->GrSave(); Clip(pPage, bEoFill); if (pShadingExtGrState) pPage->SetExtGrState(pShadingExtGrState); pPage->DrawShading(pShading); pPage->GrRestore(); } } } m_vCommands.clear(); if (bStroke) { bool bPath = false; std::vector arrForStroke; std::vector> rectangles; for (int i = 0; i < arrRedact.size(); i += 8) { arrForStroke.push_back(PdfWriter::CSegment(PdfWriter::CPoint(arrRedact[i + 0], arrRedact[i + 1]), PdfWriter::CPoint(arrRedact[i + 2], arrRedact[i + 3]))); arrForStroke.push_back(PdfWriter::CSegment(PdfWriter::CPoint(arrRedact[i + 2], arrRedact[i + 3]), PdfWriter::CPoint(arrRedact[i + 4], arrRedact[i + 5]))); arrForStroke.push_back(PdfWriter::CSegment(PdfWriter::CPoint(arrRedact[i + 4], arrRedact[i + 5]), PdfWriter::CPoint(arrRedact[i + 6], arrRedact[i + 7]))); arrForStroke.push_back(PdfWriter::CSegment(PdfWriter::CPoint(arrRedact[i + 6], arrRedact[i + 7]), PdfWriter::CPoint(arrRedact[i + 0], arrRedact[i + 1]))); std::vector rectangle = { PdfWriter::CPoint(arrRedact[i + 0], arrRedact[i + 1]), PdfWriter::CPoint(arrRedact[i + 2], arrRedact[i + 3]), PdfWriter::CPoint(arrRedact[i + 4], arrRedact[i + 5]), PdfWriter::CPoint(arrRedact[i + 6], arrRedact[i + 7]) }; rectangles.push_back(rectangle); } size_t length = oPath.GetPointCount(), compound = oPath.GetCloseCount(); std::vector points = oPath.GetPoints(0, length + compound); double dXStart = -1, dYStart = -1, dXCur = -1, dYCur = -1;; for (size_t i = 0; i < length + compound; i++) { if (oPath.IsCurvePoint(i)) { double dX = points[i].X; double dY = points[i].Y; double dX2 = points[i + 1].X; double dY2 = points[i + 1].Y; double dX3 = points[i + 2].X; double dY3 = points[i + 2].Y; i += 2; Aggplus::CGraphicsPath _oPath; _oPath.StartFigure(); _oPath.MoveTo(dXCur, dYCur); _oPath.CurveTo(dX, dY, dX2, dY2, dX3, dY3); _oPath.CloseFigure(); arrForStroke.push_back(PdfWriter::CSegment(PdfWriter::CPoint(dXCur, dYCur), PdfWriter::CPoint(dX3, dY3))); dXCur = dX3, dYCur = dY3; oPathResult = Aggplus::CalcBooleanOperation(_oPath, oPathRedact, Aggplus::BooleanOpType::Subtraction); bPath = DrawPathRedact(pMatrix, &oPathResult, bStroke, arrForStroke) || bPath; oPathResult.Reset(); } else if (oPath.IsMovePoint(i)) { double dX = points[i].X, dY = points[i].Y; dXStart = dX; dYStart = dY; dXCur = dX; dYCur = dY; } else if (oPath.IsLinePoint(i)) { double dX = points[i].X, dY = points[i].Y; PdfWriter::CSegment line(PdfWriter::CPoint(dXCur, dYCur), PdfWriter::CPoint(dX, dY)); dXCur = dX; dYCur = dY; auto visibleSegments = PdfWriter::RectangleIntersection::findSegmentsOutsideRectangles(line, rectangles); bPath = visibleSegments.size() != 0 || bPath; for (int i = 0; i < visibleSegments.size(); ++i) { double dX1 = visibleSegments[i].start.x, dY1 = visibleSegments[i].start.y; double dX2 = visibleSegments[i].end.x, dY2 = visibleSegments[i].end.y; oInverse.Apply(dX1, dY1); oInverse.Apply(dX2, dY2); MoveTo(dX1, dY1); LineTo(dX2, dY2); } } else if (oPath.IsClosePoint(i)) { PdfWriter::CSegment line(PdfWriter::CPoint(dXCur, dYCur), PdfWriter::CPoint(dXStart, dYStart)); auto visibleSegments = PdfWriter::RectangleIntersection::findSegmentsOutsideRectangles(line, rectangles); bPath = visibleSegments.size() != 0 || bPath; for (int i = 0; i < visibleSegments.size(); ++i) { double dX1 = visibleSegments[i].start.x, dY1 = visibleSegments[i].start.y; double dX2 = visibleSegments[i].end.x, dY2 = visibleSegments[i].end.y; oInverse.Apply(dX1, dY1); oInverse.Apply(dX2, dY2); MoveTo(dX1, dY1); LineTo(dX2, dY2); } } } if (bPath) Draw(pPage, bStroke, false, false); } m_vCommands = vCommands; } void CPath::Draw(PdfWriter::CPage* pPage, bool bStroke, bool bFill, bool bEoFill) { for (int nIndex = 0, nCount = m_vCommands.size(); nIndex < nCount; nIndex++) { CPathCommandBase* pCommand = m_vCommands.at(nIndex); pCommand->Draw(pPage); } if (bStroke && !bFill && !bEoFill) pPage->Stroke(); else if (bStroke && bFill) pPage->FillStroke(); else if (bStroke && bEoFill) pPage->EoFillStroke(); else if (bFill) pPage->Fill(); else if (bEoFill) pPage->EoFill(); else pPage->EndPath(); } void CPath::Clip(PdfWriter::CPage* pPage, bool bEvenOdd) { for (int nIndex = 0, nCount = m_vCommands.size(); nIndex < nCount; nIndex++) { CPathCommandBase* pCommand = m_vCommands.at(nIndex); pCommand->Draw(pPage); } if (bEvenOdd) pPage->Eoclip(); else pPage->Clip(); pPage->EndPath(); } void CPath::GetLastPoint(double& dX, double& dY) { dX = 0; dY = 0; bool bFindMoveTo = false; for (int nIndex = m_vCommands.size() - 1; nIndex >= 0; nIndex--) { CPathCommandBase* pCommand = m_vCommands.at(nIndex); if (rendererpathcommand_Close == pCommand->GetType()) { bFindMoveTo = true; continue; } else { pCommand->GetLastPoint(dX, dY); if (!bFindMoveTo || rendererpathcommand_MoveTo == pCommand->GetType()) break; } } } void CPath::GetBounds(double& dL, double& dT, double& dR, double& dB) { GetLastPoint(dL, dT); dR = dL; dB = dT; for (int nIndex = 0, nCount = m_vCommands.size(); nIndex < nCount; nIndex++) { CPathCommandBase* pCommand = m_vCommands.at(nIndex); pCommand->UpdateBounds(dL, dT, dR, dB); } } void CPath::CPathMoveTo::Draw(PdfWriter::CPage* pPage) { pPage->MoveTo(x, y); } void CPath::CPathMoveTo::UpdateBounds(double& dL, double& dT, double& dR, double& dB) { UpdateMaxMinPoints(dL, dT, dR, dB, x, y); } void CPath::CPathMoveTo::ToCGraphicsPath(PdfWriter::CMatrix* pMatrix, Aggplus::CGraphicsPath& oPath) { double dX = x, dY = y; pMatrix->Apply(dX, dY); oPath.StartFigure(); oPath.MoveTo(dX, dY); } void CPath::CPathLineTo::Draw(PdfWriter::CPage* pPage) { pPage->LineTo(x, y); } void CPath::CPathLineTo::UpdateBounds(double& dL, double& dT, double& dR, double& dB) { UpdateMaxMinPoints(dL, dT, dR, dB, x, y); } void CPath::CPathLineTo::ToCGraphicsPath(PdfWriter::CMatrix* pMatrix, Aggplus::CGraphicsPath& oPath) { double dX, dY; pMatrix->Transform(x, y, &dX, &dY); oPath.LineTo(dX, dY); } void CPath::CPathCurveTo::Draw(PdfWriter::CPage* pPage) { pPage->CurveTo(x1, y1, x2, y2, xe, ye); } void CPath::CPathCurveTo::UpdateBounds(double& dL, double& dT, double& dR, double& dB) { UpdateMaxMinPoints(dL, dT, dR, dB, x1, y1); UpdateMaxMinPoints(dL, dT, dR, dB, x2, y2); UpdateMaxMinPoints(dL, dT, dR, dB, xe, ye); } void CPath::CPathCurveTo::ToCGraphicsPath(PdfWriter::CMatrix* pMatrix, Aggplus::CGraphicsPath& oPath) { double dX1, dY1, dX2, dY2, dX3, dY3; pMatrix->Transform(x1, y1, &dX1, &dY1); pMatrix->Transform(x2, y2, &dX2, &dY2); pMatrix->Transform(xe, ye, &dX3, &dY3); oPath.CurveTo(dX1, dY1, dX2, dY2, dX3, dY3); } void CPath::CPathArcTo::Draw(PdfWriter::CPage* pPage) { if (sweepAngle >= 360 - 0.001) pPage->Ellipse(x + w / 2, y + h / 2, w / 2, h / 2); else pPage->EllipseArcTo(x + w / 2, y + h / 2, w / 2, h / 2, 360 - startAngle, 360 - (startAngle + sweepAngle), sweepAngle > 0 ? true : false); } void CPath::CPathArcTo::UpdateBounds(double& dL, double& dT, double& dR, double& dB) { UpdateMaxMinPoints(dL, dT, dR, dB, x, y); UpdateMaxMinPoints(dL, dT, dR, dB, x + w, y + h); } double AngToEllPrm(double dAngle, double dXRad, double dYRad) { // Функция для перевода реального угла в параметрическое задание эллписа // т.е. 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(dAngle) / dYRad, cos(dAngle) / dXRad); } void WriteEllipseArc(PdfWriter::CMatrix* pMatrix, Aggplus::CGraphicsPath& oPath, double dX, double dY, double dXRad, double dYRad, double dAngle1, double dAngle2, double& dXCur, double& dYCur, bool bClockDirection = false) { // Рассчитаем начальную, конечную и контрольные точки double dX1 = 0.0, dX2 = 0.0, dY1 = 0.0, dY2 = 0.0; double dCX1 = 0.0, dCX2 = 0.0, dCY1 = 0.0, dCY2 = 0.0; double dAlpha = sin(dAngle2 - dAngle1) * (sqrt(4.0 + 3.0 * tan((dAngle2 - dAngle1) / 2.0) * tan((dAngle2 - dAngle1) / 2.0)) - 1.0) / 3.0; dX1 = dX + dXRad * cos(dAngle1); dY1 = dY + dYRad * sin(dAngle1); dX2 = dX + dXRad * cos(dAngle2); dY2 = dY + dYRad * sin(dAngle2); dCX1 = dX1 - dAlpha * dXRad * sin(dAngle1); dCY1 = dY1 + dAlpha * dYRad * cos(dAngle1); dCX2 = dX2 + dAlpha * dXRad * sin(dAngle2); dCY2 = dY2 - dAlpha * dYRad * cos(dAngle2); if ( !bClockDirection ) { dXCur = dX2; dYCur = dY2; double _dX1, _dY1, _dX2, _dY2, dX3, dY3; pMatrix->Transform(dCX1, dCY1, &_dX1, &_dY1); pMatrix->Transform(dCX2, dCY2, &_dX2, &_dY2); pMatrix->Transform(dX2, dY2, &dX3, &dY3); oPath.CurveTo(_dX1, _dY1, _dX2, _dY2, dX3, dY3); } else { dXCur = dX1; dYCur = dY1; double _dX1, _dY1, _dX2, _dY2, dX3, dY3; pMatrix->Transform(dCX2, dCY2, &_dX1, &_dY1); pMatrix->Transform(dCX1, dCY1, &_dX2, &_dY2); pMatrix->Transform(dX1, dY1, &dX3, &dY3); oPath.CurveTo(_dX1, _dY1, _dX2, _dY2, dX3, dY3); } } void EllipseArc(double dX, double dY, double dXRad, double dYRad, double _dAngle1, double _dAngle2, bool bClockDirection, PdfWriter::CMatrix* pMatrix, Aggplus::CGraphicsPath& oPath) { // переведем углы в радианы double dAngle1 = _dAngle1 * 3.141592f / 180; double dAngle2 = _dAngle2 * 3.141592f / 180; // Выясним в каких четвертях находятся начальная и конечная точки int nFirstPointQuard = int(_dAngle1) / 90 + 1; int nSecondPointQuard = int(_dAngle2) / 90 + 1; nSecondPointQuard = std::min(4, std::max(1, nSecondPointQuard)); nFirstPointQuard = std::min(4, std::max(1, nFirstPointQuard)); // Проведем линию в начальную точку дуги double dStartX = 0.0, dStartY = 0.0, dEndX = 0.0, dEndY = 0.0; dStartX = dX + dXRad * cos(AngToEllPrm(dAngle1, dXRad, dYRad)); dStartY = dY + dYRad * sin(AngToEllPrm(dAngle1, dXRad, dYRad)); // Дальше рисуем по четверям double dCurX = dStartX, dCurY = dStartY; 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; WriteEllipseArc(pMatrix, oPath, dX, dY, dXRad, dYRad, AngToEllPrm(dStartAngle, dXRad, dYRad), AngToEllPrm(dEndAngle, dXRad, dYRad), dEndX, dEndY, 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; WriteEllipseArc(pMatrix, oPath, dX, dY, dXRad, dYRad, AngToEllPrm(dStartAngle, dXRad, dYRad), AngToEllPrm(dEndAngle, dXRad, dYRad), dEndX, dEndY, false); } } } void CPath::CPathArcTo::ToCGraphicsPath(PdfWriter::CMatrix* pMatrix, Aggplus::CGraphicsPath& oPath) { if (sweepAngle >= 360 - 0.001) { const double c_dKappa = 0.552; double dX = x + w / 2; double dY = y + h / 2; double dXRay = w / 2; double dYRay = h / 2; double _dX, _dY; pMatrix->Transform(dX, dY, &_dX, &_dY); oPath.MoveTo(_dX, _dY); double dX1, dY1, dX2, dY2, dX3, dY3; pMatrix->Transform(dX - dXRay, dY + dYRay * c_dKappa, &dX1, &dY1); pMatrix->Transform(dX - dXRay * c_dKappa, dY + dYRay, &dX2, &dY2); pMatrix->Transform(dX, dY + dYRay, &dX3, &dY3); oPath.CurveTo(dX1, dY1, dX2, dY2, dX3, dY3); pMatrix->Transform(dX + dXRay * c_dKappa, dY + dYRay, &dX1, &dY1); pMatrix->Transform(dX + dXRay, dY + dYRay * c_dKappa, &dX2, &dY2); pMatrix->Transform(dX + dXRay, dY, &dX3, &dY3); oPath.CurveTo(dX1, dY1, dX2, dY2, dX3, dY3); pMatrix->Transform(dX + dXRay, dY - dYRay * c_dKappa, &dX1, &dY1); pMatrix->Transform(dX + dXRay * c_dKappa, dY - dYRay, &dX2, &dY2); pMatrix->Transform(dX, dY - dYRay, &dX3, &dY3); oPath.CurveTo(dX1, dY1, dX2, dY2, dX3, dY3); pMatrix->Transform(dX - dXRay * c_dKappa, dY - dYRay, &dX1, &dY1); pMatrix->Transform(dX - dXRay, dY - dYRay * c_dKappa, &dX2, &dY2); pMatrix->Transform(dX - dXRay, dY, &dX3, &dY3); oPath.CurveTo(dX1, dY1, dX2, dY2, dX3, dY3); } else { double dX = x + w / 2, dY = y + h / 2, dXRad = w / 2, dYRad = h / 2, _dAngle1 = 360 - startAngle, _dAngle2 = 360 - (startAngle + sweepAngle); bool bClockDirection = sweepAngle > 0; // Проверяем эллипс на невырожденность if (dXRad < 0.001 || dYRad < 0.001) { double dAngle1 = _dAngle1 * 3.141592f / 180; double dAngle2 = _dAngle2 * 3.141592f / 180; double _dX, _dY; if (dXRad < 0.001 && dYRad < 0.001) { pMatrix->Transform(dX, dY, &_dX, &_dY); oPath.LineTo(_dX, _dY); } else if (dXRad < 0.001) { pMatrix->Transform(dX, dY + sin(dAngle1) * dYRad, &_dX, &_dY); oPath.LineTo(_dX, _dY); pMatrix->Transform(dX, dY + sin(dAngle2) * dYRad, &_dX, &_dY); oPath.LineTo(_dX, _dY); } else // if (dYRad < 0.001) { pMatrix->Transform(dX + cos(dAngle1) * dXRad, dY, &_dX, &_dY); oPath.LineTo(_dX, _dY); pMatrix->Transform(dX + cos(dAngle2) * dXRad, dY, &_dX, &_dY); oPath.LineTo(_dX, _dY); } return; } while (_dAngle1 < 0) _dAngle1 += 360; while (_dAngle1 > 360) _dAngle1 -= 360; while (_dAngle2 < 0) _dAngle2 += 360; while (_dAngle2 > 360) _dAngle2 -= 360; if (!bClockDirection) { if (_dAngle1 <= _dAngle2) EllipseArc(dX, dY, dXRad, dYRad, _dAngle1, _dAngle2, false, pMatrix, oPath); else { EllipseArc(dX, dY, dXRad, dYRad, _dAngle1, 360, false, pMatrix, oPath); EllipseArc(dX, dY, dXRad, dYRad, 0, _dAngle2, false, pMatrix, oPath); } } else { if (_dAngle1 >= _dAngle2) EllipseArc(dX, dY, dXRad, dYRad, _dAngle1, _dAngle2, true, pMatrix, oPath); else { EllipseArc(dX, dY, dXRad, dYRad, _dAngle1, 0, true, pMatrix, oPath); EllipseArc(dX, dY, dXRad, dYRad, 360, _dAngle2, true, pMatrix, oPath); } } } } void CPath::CPathClose::Draw(PdfWriter::CPage* pPage) { pPage->ClosePath(); } void CPath::CPathClose::UpdateBounds(double& dL, double& dT, double& dR, double& dB) { } void CPath::CPathClose::ToCGraphicsPath(PdfWriter::CMatrix* pMatrix, Aggplus::CGraphicsPath& oPath) { oPath.CloseFigure(); } void CPath::CPathText::Draw(PdfWriter::CPage* pPage) { // TODO: Если данная команда будет часто вызываться, тогда ее нужно будет оптимизировать, точно также как это делается в обычном тексте pPage->BeginText(); pPage->SetFontAndSize(font, fontSize); pPage->SetCharSpace(charSpace); pPage->SetTextRenderingMode(PdfWriter::textrenderingmode_Stroke); pPage->DrawText(x, y, codes, codesCount); pPage->EndText(); } void CPath::CPathText::UpdateBounds(double& dL, double& dT, double& dR, double& dB) { UpdateMaxMinPoints(dL, dT, dR, dB, x, y); } void CPath::CPathText::ToCGraphicsPath(PdfWriter::CMatrix* pMatrix, Aggplus::CGraphicsPath& oPath) { // Весь текст проверяется в CPdfWriter::PathCommandDrawText // Эта функция не должна быть вызвана } void CBrushState::Reset() { m_lType = c_BrushTypeSolid; m_oColor1.Set(0); m_oColor2.Set(0); m_nAlpha1 = 255; m_nAlpha2 = 255; m_wsTexturePath = L""; m_lTextureMode = c_BrushTextureModeStretch; m_nTextureAlpha = 255; m_dLinearAngle = 0; m_oRect.Reset(); if (m_pShadingColors) delete[] m_pShadingColors; if (m_pShadingPoints) delete[] m_pShadingPoints; m_pShadingColors = NULL; m_pShadingPoints = NULL; m_lShadingPointsCount = 0; }