Files
DocumentServer-v-9.2.0/core/PdfFile/SrcWriter/States.cpp
Yajbir Singh f1b860b25c
Some checks failed
check / markdownlint (push) Has been cancelled
check / spellchecker (push) Has been cancelled
updated
2025-12-11 19:03:17 +05:30

929 lines
31 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

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

/*
* (c) Copyright Ascensio System SIA 2010-2023
*
* This program is a free software product. You can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License (AGPL)
* version 3 as published by the Free Software Foundation. In accordance with
* Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect
* that Ascensio System SIA expressly excludes the warranty of non-infringement
* of any third-party rights.
*
* This program is distributed WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For
* details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
*
* You can contact Ascensio System SIA at 20A-6 Ernesta Birznieka-Upish
* street, Riga, Latvia, EU, LV-1050.
*
* The interactive user interfaces in modified source and object code versions
* of the Program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU AGPL version 3.
*
* Pursuant to Section 7(b) of the License you must retain the original Product
* logo when distributing the program. Pursuant to Section 7(e) we decline to
* grant you any rights under trademark law for use of our trademarks.
*
* All the Product's GUI elements, including illustrations and icon sets, as
* well as technical writing content are licensed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International. See the License
* terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
*
*/
#include "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<PdfWriter::CSegment>& 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<PdfWriter::CSegment>& arrForStroke)
{
PdfWriter::CMatrix oInverse = pMatrix->Inverse();
size_t length = oPath->GetPointCount(), compound = oPath->GetCloseCount();
std::vector<Aggplus::PointD> 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<double>& 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<CPathCommandBase*> 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<PdfWriter::CSegment> arrForStroke;
std::vector<std::vector<PdfWriter::CPoint>> 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<PdfWriter::CPoint> 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<Aggplus::PointD> 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;
}