Files
core/PdfFile/SrcWriter/States.cpp
Alexander Trofimov 76ee07f61c [copyright] Update copyright header
Co-authored-by: Alexander Trofimov <alexander.trofimov@onlyoffice.com>
Co-committed-by: Alexander Trofimov <alexander.trofimov@onlyoffice.com>
2026-05-14 08:23:56 +00:00

938 lines
30 KiB
C++

/*
* Copyright (C) Ascensio System SIA, 2009-2026
*
* 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, together with the
* additional terms provided in the LICENSE file.
*
* 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: https://www.gnu.org/licenses/agpl-3.0.html
*
* You can contact Ascensio System SIA by email at info@onlyoffice.com
* or by postal mail at 20A-6 Ernesta Birznieka-Upisha Street, Riga,
* LV-1050, Latvia, European Union.
*
* The interactive user interfaces in modified versions of the Program
* are required to display Appropriate Legal Notices in accordance with
* Section 5 of the GNU AGPL version 3.
*
* No trademark rights are granted under this License.
*
* All non-code elements of the Product, including illustrations,
* icon sets, and technical writing content, are licensed under the
* Creative Commons Attribution-ShareAlike 4.0 International License:
* https://creativecommons.org/licenses/by-sa/4.0/legalcode
*
* This license applies only to such non-code elements and does not
* modify or replace the licensing terms applicable to the Program's
* source code, which remains licensed under the GNU Affero General
* Public License v3.
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
#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();
}
//----------------------------------------------------------------------------------------
// Internal functions
//----------------------------------------------------------------------------------------
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;
// Calculate coefficients A, B, C for line equation 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;
// Check if point P1 lies on line P3P4
double check1 = A * P1.x + B * P1.y + C;
// Check if point P2 lies on line P3P4
double check2 = A * P2.x + B * P2.y + C;
// If both checks are close to zero (within epsilon), it lies on the line
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)
{
// Function to convert real angle to parametric ellipse representation
// i.e. x= a cos(t) y = b sin(t) - parametric ellipse representation.
// x = r cos(p), y = r sin(p) => t = atan2( sin(p) / b, cos(p) / a );
return atan2(sin(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)
{
// Calculate start, end and control points
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)
{
// Convert angles to radians
double dAngle1 = _dAngle1 * 3.141592f / 180;
double dAngle2 = _dAngle2 * 3.141592f / 180;
// Determine which quadrants contain the start and end points
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));
// Draw line to arc start point
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));
// Continue drawing by quadrants
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;
// Check ellipse for degeneracy
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: If this command is called frequently, it needs to be optimized, the same way as regular text
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)
{
// All text is checked in CPdfWriter::PathCommandDrawText
// This function should not be called
}
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;
m_bIsScale = false;
m_dScaleX = 1.0;
m_dScaleY = 1.0;
m_dOffsetX = 0.0;
m_dOffsetY = 0.0;
}