mirror of
https://github.com/ONLYOFFICE/core.git
synced 2026-02-10 18:05:41 +08:00
Redact writer
This commit is contained in:
@ -904,7 +904,7 @@ HRESULT CPdfWriter::DrawPath(NSFonts::IApplicationFonts* pAppFonts, const std::w
|
||||
}
|
||||
|
||||
if (!m_arrRedact.empty())
|
||||
m_oPath.Redact(m_oTransform, m_arrRedact);
|
||||
m_oPath.Redact(m_oTransform, m_arrRedact, bStroke, bEoFill);
|
||||
|
||||
if (!m_pShading)
|
||||
{
|
||||
|
||||
@ -897,190 +897,6 @@ void RedactOutputDev::DrawPathRedact(Aggplus::CGraphicsPath* oPath, bool bStroke
|
||||
}
|
||||
}
|
||||
}
|
||||
class CLineClipper
|
||||
{
|
||||
private:
|
||||
std::vector<TRect> rectangles;
|
||||
|
||||
// Проверка пересечения отрезка с прямоугольником
|
||||
bool intersectsRectangle(const CSegment& segment, const TRect& rect)
|
||||
{
|
||||
// Используем алгоритм Коэна-Сазерленда для определения пересечения
|
||||
int code1 = computeCohenSutherlandCode(segment.start, rect);
|
||||
int code2 = computeCohenSutherlandCode(segment.end, rect);
|
||||
|
||||
// Если оба конца внутри - полное пересечение
|
||||
if (code1 == 0 && code2 == 0) return true;
|
||||
|
||||
// Если оба конца с одной стороны - нет пересечения
|
||||
if ((code1 & code2) != 0) return false;
|
||||
|
||||
// Есть пересечение
|
||||
return true;
|
||||
}
|
||||
|
||||
// Вычисление кода Коэна-Сазерленда
|
||||
int computeCohenSutherlandCode(const CPoint& p, const TRect& rect)
|
||||
{
|
||||
int code = 0;
|
||||
|
||||
if (p.x < rect.fLeft) code |= 1; // Слева
|
||||
if (p.x > rect.fRight) code |= 2; // Справа
|
||||
if (p.y < rect.fBottom) code |= 4; // Снизу
|
||||
if (p.y > rect.fTop) code |= 8; // Сверху
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
// Нахождение точек пересечения отрезка с прямоугольником
|
||||
std::vector<CPoint> findIntersectionPoints(const CSegment& segment, const TRect& rect)
|
||||
{
|
||||
std::vector<CPoint> intersections;
|
||||
|
||||
// Проверяем пересечение с каждой стороной прямоугольника
|
||||
std::vector<CSegment> rectSides =
|
||||
{
|
||||
CSegment(CPoint(rect.fLeft, rect.fBottom), CPoint(rect.fRight, rect.fBottom)), // нижняя
|
||||
CSegment(CPoint(rect.fRight, rect.fBottom), CPoint(rect.fRight, rect.fTop)), // правая
|
||||
CSegment(CPoint(rect.fRight, rect.fTop), CPoint(rect.fLeft, rect.fTop)), // верхняя
|
||||
CSegment(CPoint(rect.fLeft, rect.fTop), CPoint(rect.fLeft, rect.fBottom)) // левая
|
||||
};
|
||||
|
||||
for (const auto& side : rectSides)
|
||||
{
|
||||
CPoint intersection;
|
||||
if (findLineIntersection(segment, side, intersection))
|
||||
{
|
||||
// Проверяем, что точка лежит на обоих отрезках
|
||||
if (pointOnSegment(intersection, segment) && pointOnSegment(intersection, side))
|
||||
intersections.push_back(intersection);
|
||||
}
|
||||
}
|
||||
|
||||
return intersections;
|
||||
}
|
||||
|
||||
// Нахождение пересечения двух отрезков
|
||||
bool findLineIntersection(const CSegment& seg1, const CSegment& seg2, CPoint& result)
|
||||
{
|
||||
double x1 = seg1.start.x, y1 = seg1.start.y;
|
||||
double x2 = seg1.end.x, y2 = seg1.end.y;
|
||||
double x3 = seg2.start.x, y3 = seg2.start.y;
|
||||
double x4 = seg2.end.x, y4 = seg2.end.y;
|
||||
|
||||
double denom = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
|
||||
|
||||
if (std::abs(denom) < 1e-9) return false; // Параллельные линии
|
||||
|
||||
double t = ((x1 - x3) * (y3 - y4) - (y1 - y3) * (x3 - x4)) / denom;
|
||||
double u = -((x1 - x2) * (y1 - y3) - (y1 - y2) * (x1 - x3)) / denom;
|
||||
|
||||
result.x = x1 + t * (x2 - x1);
|
||||
result.y = y1 + t * (y2 - y1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Проверка, лежит ли точка на отрезке
|
||||
bool pointOnSegment(const CPoint& p, const CSegment& seg)
|
||||
{
|
||||
// Проверяем коллинеарность и нахождение между концами
|
||||
return std::abs(distance(seg.start, p) + distance(p, seg.end) - distance(seg.start, seg.end)) < 1e-9;
|
||||
}
|
||||
|
||||
// Расстояние между двумя точками
|
||||
double distance(const CPoint& a, const CPoint& b)
|
||||
{
|
||||
return std::sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
|
||||
}
|
||||
|
||||
// Параметрическое значение точки на линии (0 = start, 1 = end)
|
||||
double getParametricValue(const CPoint& p, const CSegment& line)
|
||||
{
|
||||
double lineLength = distance(line.start, line.end);
|
||||
if (lineLength < 1e-9) return 0;
|
||||
|
||||
// Проекция на направляющий вектор линии
|
||||
double dx = line.end.x - line.start.x;
|
||||
double dy = line.end.y - line.start.y;
|
||||
|
||||
if (std::abs(dx) > std::abs(dy))
|
||||
return (p.x - line.start.x) / dx;
|
||||
else
|
||||
return (p.y - line.start.y) / dy;
|
||||
}
|
||||
|
||||
public:
|
||||
CLineClipper(const std::vector<TRect>& rects) : rectangles(rects) {}
|
||||
|
||||
// Основная функция для вычисления видимых сегментов
|
||||
std::vector<CSegment> getVisibleSegments(const CSegment& line)
|
||||
{
|
||||
std::vector<double> cutPoints = {0.0, 1.0}; // Начало и конец линии
|
||||
|
||||
// Собираем все точки пересечения со всеми прямоугольниками
|
||||
for (const auto& rect : rectangles)
|
||||
{
|
||||
if (intersectsRectangle(line, rect))
|
||||
{
|
||||
auto intersections = findIntersectionPoints(line, rect);
|
||||
for (const auto& p : intersections)
|
||||
{
|
||||
double t = getParametricValue(p, line);
|
||||
if (t >= 0 && t <= 1)
|
||||
cutPoints.push_back(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Сортируем точки по параметрическому значению
|
||||
std::sort(cutPoints.begin(), cutPoints.end());
|
||||
cutPoints.erase(std::unique(cutPoints.begin(), cutPoints.end()), cutPoints.end());
|
||||
|
||||
// Проверяем каждый сегмент на видимость
|
||||
std::vector<CSegment> visibleSegments;
|
||||
for (size_t i = 0; i < cutPoints.size() - 1; i++)
|
||||
{
|
||||
double t1 = cutPoints[i];
|
||||
double t2 = cutPoints[i + 1];
|
||||
|
||||
// Средняя точка сегмента для проверки видимости
|
||||
double t_mid = (t1 + t2) / 2.0;
|
||||
CPoint midPoint(
|
||||
line.start.x + t_mid * (line.end.x - line.start.x),
|
||||
line.start.y + t_mid * (line.end.y - line.start.y)
|
||||
);
|
||||
|
||||
// Сегмент видим, если его средняя точка не внутри ни одного прямоугольника
|
||||
if (!isPointInsideAnyRectangle(midPoint))
|
||||
{
|
||||
CPoint segStart(
|
||||
line.start.x + t1 * (line.end.x - line.start.x),
|
||||
line.start.y + t1 * (line.end.y - line.start.y)
|
||||
);
|
||||
CPoint segEnd(
|
||||
line.start.x + t2 * (line.end.x - line.start.x),
|
||||
line.start.y + t2 * (line.end.y - line.start.y)
|
||||
);
|
||||
visibleSegments.push_back(CSegment(segStart, segEnd));
|
||||
}
|
||||
}
|
||||
|
||||
return visibleSegments;
|
||||
}
|
||||
|
||||
private:
|
||||
// Проверка, находится ли точка внутри любого прямоугольника
|
||||
bool isPointInsideAnyRectangle(const CPoint& p)
|
||||
{
|
||||
for (const auto& rect : rectangles)
|
||||
{
|
||||
if (p.x >= rect.fLeft && p.x <= rect.fRight && p.y >= rect.fBottom && p.y <= rect.fTop)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
void RedactOutputDev::DoPathRedact(GfxState* pGState, GfxPath* pPath, double* pCTM, bool bStroke, bool bEoFill)
|
||||
{
|
||||
double arrMatrix[6];
|
||||
|
||||
@ -303,15 +303,235 @@ static inline void UpdateMaxMinPoints(double& dMinX, double& dMinY, double& dMax
|
||||
if (dY > dMaxY)
|
||||
dMaxY = dY;
|
||||
}
|
||||
void CPath::Redact(const CTransform& oTransform, const std::vector<double>& arrRedact)
|
||||
bool SkipPath(const std::vector<PdfWriter::CSegment>& arrForStroke, const PdfWriter::CPoint& P1, const PdfWriter::CPoint& P2)
|
||||
{
|
||||
Aggplus::CGraphicsPath oPath;
|
||||
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;
|
||||
}
|
||||
void CPath::DrawPathRedact(PdfWriter::CMatrix oMatrix, Aggplus::CGraphicsPath* oPath, bool bStroke, const std::vector<PdfWriter::CSegment>& arrForStroke)
|
||||
{
|
||||
PdfWriter::CMatrix oInverse = oMatrix.Inverse();
|
||||
|
||||
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;
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
void CPath::Redact(const CTransform& oTransform, const std::vector<double>& arrRedact, bool bStroke, bool bEoFill)
|
||||
{
|
||||
PdfWriter::CMatrix oMatrix(oTransform.m11, oTransform.m12, oTransform.m21, oTransform.m22, oTransform.dx, oTransform.dy);
|
||||
PdfWriter::CMatrix oInverse = oMatrix.Inverse();
|
||||
|
||||
Aggplus::CGraphicsPath oPath, oPathRedact, oPathResult;
|
||||
for (int i = 0; i < arrRedact.size(); i += 4)
|
||||
{
|
||||
oPathRedact.StartFigure();
|
||||
oPathRedact.MoveTo(arrRedact[i + 0], arrRedact[i + 1]);
|
||||
oPathRedact.LineTo(arrRedact[i + 0], arrRedact[i + 3]);
|
||||
oPathRedact.LineTo(arrRedact[i + 2], arrRedact[i + 3]);
|
||||
oPathRedact.LineTo(arrRedact[i + 2], arrRedact[i + 1]);
|
||||
oPathRedact.CloseFigure();
|
||||
}
|
||||
|
||||
for (int nIndex = 0, nCount = m_vCommands.size(); nIndex < nCount; nIndex++)
|
||||
{
|
||||
CPathCommandBase* pCommand = m_vCommands.at(nIndex);
|
||||
// CPath to CGraphicsPath
|
||||
pCommand->ToCGraphicsPath(oTransform, oPath);
|
||||
}
|
||||
if (bEoFill)
|
||||
oPath.SetRuler(true);
|
||||
Clear();
|
||||
|
||||
if (bStroke)
|
||||
{
|
||||
std::vector<PdfWriter::CSegment> arrForStroke;
|
||||
std::vector<PdfWriter::TRect> rectangles;
|
||||
for (int i = 0; i < arrRedact.size(); i += 4)
|
||||
{
|
||||
arrForStroke.push_back(PdfWriter::CSegment(PdfWriter::CPoint(arrRedact[i + 0], arrRedact[i + 1]), PdfWriter::CPoint(arrRedact[i + 0], arrRedact[i + 3])));
|
||||
arrForStroke.push_back(PdfWriter::CSegment(PdfWriter::CPoint(arrRedact[i + 0], arrRedact[i + 3]), 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 + 2], arrRedact[i + 1])));
|
||||
arrForStroke.push_back(PdfWriter::CSegment(PdfWriter::CPoint(arrRedact[i + 2], arrRedact[i + 1]), PdfWriter::CPoint(arrRedact[i + 0], arrRedact[i + 1])));
|
||||
|
||||
rectangles.push_back({arrRedact[i + 0], arrRedact[i + 3], arrRedact[i + 2], arrRedact[i + 1]});
|
||||
}
|
||||
|
||||
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);
|
||||
DrawPathRedact(oMatrix, &oPathResult, bStroke, arrForStroke);
|
||||
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::CLineClipper clipper(rectangles);
|
||||
PdfWriter::CSegment line(PdfWriter::CPoint(dXCur, dYCur), PdfWriter::CPoint(dX, dY));
|
||||
dXCur = dX; dYCur = dY;
|
||||
|
||||
auto visibleSegments = clipper.getVisibleSegments(line);
|
||||
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::CLineClipper clipper(rectangles);
|
||||
PdfWriter::CSegment line(PdfWriter::CPoint(dXCur, dYCur), PdfWriter::CPoint(dXStart, dYStart));
|
||||
auto visibleSegments = clipper.getVisibleSegments(line);
|
||||
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
|
||||
{
|
||||
oPathResult = Aggplus::CalcBooleanOperation(oPath, oPathRedact, Aggplus::BooleanOpType::Subtraction);
|
||||
DrawPathRedact(oMatrix, &oPathResult, bStroke);
|
||||
}
|
||||
}
|
||||
void CPath::Draw(PdfWriter::CPage* pPage, bool bStroke, bool bFill, bool bEoFill)
|
||||
{
|
||||
@ -395,6 +615,7 @@ void CPath::CPathMoveTo::ToCGraphicsPath(const CTransform& oTransform, Aggplus::
|
||||
{
|
||||
double dX, dY;
|
||||
oTransform.Transform(x, y, &dX, &dY);
|
||||
oPath.StartFigure();
|
||||
oPath.MoveTo(dX, dY);
|
||||
}
|
||||
void CPath::CPathLineTo::Draw(PdfWriter::CPage* pPage)
|
||||
@ -441,9 +662,213 @@ void CPath::CPathArcTo::UpdateBounds(double& dL, double& dT, double& dR, double&
|
||||
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(const CTransform& oTransform, 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;
|
||||
oTransform.Transform(dCX1, dCY1, &_dX1, &_dY1);
|
||||
oTransform.Transform(dCX2, dCY2, &_dX2, &_dY2);
|
||||
oTransform.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;
|
||||
oTransform.Transform(dCX2, dCY2, &_dX1, &_dY1);
|
||||
oTransform.Transform(dCX1, dCY1, &_dX2, &_dY2);
|
||||
oTransform.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, const CTransform& oTransform, 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(oTransform, 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(oTransform, oPath, dX, dY, dXRad, dYRad, AngToEllPrm(dStartAngle, dXRad, dYRad), AngToEllPrm(dEndAngle, dXRad, dYRad), dEndX, dEndY, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
void CPath::CPathArcTo::ToCGraphicsPath(const CTransform& oTransform, 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;
|
||||
oTransform.Transform(dX, dY, &_dX, &_dY);
|
||||
oPath.MoveTo(_dX, _dY);
|
||||
|
||||
double dX1, dY1, dX2, dY2, dX3, dY3;
|
||||
oTransform.Transform(dX - dXRay, dY + dYRay * c_dKappa, &dX1, &dY1);
|
||||
oTransform.Transform(dX - dXRay * c_dKappa, dY + dYRay, &dX2, &dY2);
|
||||
oTransform.Transform(dX, dY + dYRay, &dX3, &dY3);
|
||||
oPath.CurveTo(dX1, dY1, dX2, dY2, dX3, dY3);
|
||||
|
||||
oTransform.Transform(dX + dXRay * c_dKappa, dY + dYRay, &dX1, &dY1);
|
||||
oTransform.Transform(dX + dXRay, dY + dYRay * c_dKappa, &dX2, &dY2);
|
||||
oTransform.Transform(dX + dXRay, dY, &dX3, &dY3);
|
||||
oPath.CurveTo(dX1, dY1, dX2, dY2, dX3, dY3);
|
||||
|
||||
oTransform.Transform(dX + dXRay, dY - dYRay * c_dKappa, &dX1, &dY1);
|
||||
oTransform.Transform(dX + dXRay * c_dKappa, dY - dYRay, &dX2, &dY2);
|
||||
oTransform.Transform(dX, dY - dYRay, &dX3, &dY3);
|
||||
oPath.CurveTo(dX1, dY1, dX2, dY2, dX3, dY3);
|
||||
|
||||
oTransform.Transform(dX - dXRay * c_dKappa, dY - dYRay, &dX1, &dY1);
|
||||
oTransform.Transform(dX - dXRay, dY - dYRay * c_dKappa, &dX2, &dY2);
|
||||
oTransform.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)
|
||||
{
|
||||
oTransform.Transform(dX, dY, &_dX, &_dY);
|
||||
oPath.LineTo(_dX, _dY);
|
||||
}
|
||||
else if (dXRad < 0.001)
|
||||
{
|
||||
oTransform.Transform(dX, dY + sin(dAngle1) * dYRad, &_dX, &_dY);
|
||||
oPath.LineTo(_dX, _dY);
|
||||
oTransform.Transform(dX, dY + sin(dAngle2) * dYRad, &_dX, &_dY);
|
||||
oPath.LineTo(_dX, _dY);
|
||||
}
|
||||
else // if (dYRad < 0.001)
|
||||
{
|
||||
oTransform.Transform(dX + cos(dAngle1) * dXRad, dY, &_dX, &_dY);
|
||||
oPath.LineTo(_dX, _dY);
|
||||
oTransform.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, oTransform, oPath);
|
||||
else
|
||||
{
|
||||
EllipseArc(dX, dY, dXRad, dYRad, _dAngle1, 360, false, oTransform, oPath);
|
||||
EllipseArc(dX, dY, dXRad, dYRad, 0, _dAngle2, false, oTransform, oPath);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_dAngle1 >= _dAngle2)
|
||||
EllipseArc(dX, dY, dXRad, dYRad, _dAngle1, _dAngle2, true, oTransform, oPath);
|
||||
else
|
||||
{
|
||||
EllipseArc(dX, dY, dXRad, dYRad, _dAngle1, 0, true, oTransform, oPath);
|
||||
EllipseArc(dX, dY, dXRad, dYRad, 360, _dAngle2, true, oTransform, oPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void CPath::CPathClose::Draw(PdfWriter::CPage* pPage)
|
||||
{
|
||||
|
||||
@ -1597,7 +1597,8 @@ public:
|
||||
void Draw(PdfWriter::CPage* pPage, bool bStroke, bool bFill, bool bEoFill);
|
||||
void Clip(PdfWriter::CPage* pPage, bool bEvenOdd = false);
|
||||
void GetBounds(double& dL, double& dT, double& dR, double& dB);
|
||||
void Redact(const CTransform& oTransform, const std::vector<double>& arrRedact);
|
||||
void Redact(const CTransform& oTransform, const std::vector<double>& arrRedact, bool bStroke, bool bEoFill);
|
||||
void DrawPathRedact(PdfWriter::CMatrix oMatrix, Aggplus::CGraphicsPath* oPath, bool bStroke, const std::vector<PdfWriter::CSegment>& arrForStroke = {});
|
||||
|
||||
private:
|
||||
|
||||
|
||||
@ -393,6 +393,190 @@ namespace PdfWriter
|
||||
CPoint end;
|
||||
CSegment(const CPoint& s, const CPoint& e) : start(s), end(e) {}
|
||||
};
|
||||
class CLineClipper
|
||||
{
|
||||
private:
|
||||
std::vector<TRect> rectangles;
|
||||
|
||||
// Проверка пересечения отрезка с прямоугольником
|
||||
bool intersectsRectangle(const CSegment& segment, const TRect& rect)
|
||||
{
|
||||
// Используем алгоритм Коэна-Сазерленда для определения пересечения
|
||||
int code1 = computeCohenSutherlandCode(segment.start, rect);
|
||||
int code2 = computeCohenSutherlandCode(segment.end, rect);
|
||||
|
||||
// Если оба конца внутри - полное пересечение
|
||||
if (code1 == 0 && code2 == 0) return true;
|
||||
|
||||
// Если оба конца с одной стороны - нет пересечения
|
||||
if ((code1 & code2) != 0) return false;
|
||||
|
||||
// Есть пересечение
|
||||
return true;
|
||||
}
|
||||
|
||||
// Вычисление кода Коэна-Сазерленда
|
||||
int computeCohenSutherlandCode(const CPoint& p, const TRect& rect)
|
||||
{
|
||||
int code = 0;
|
||||
|
||||
if (p.x < rect.fLeft) code |= 1; // Слева
|
||||
if (p.x > rect.fRight) code |= 2; // Справа
|
||||
if (p.y < rect.fBottom) code |= 4; // Снизу
|
||||
if (p.y > rect.fTop) code |= 8; // Сверху
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
// Нахождение точек пересечения отрезка с прямоугольником
|
||||
std::vector<CPoint> findIntersectionPoints(const CSegment& segment, const TRect& rect)
|
||||
{
|
||||
std::vector<CPoint> intersections;
|
||||
|
||||
// Проверяем пересечение с каждой стороной прямоугольника
|
||||
std::vector<CSegment> rectSides =
|
||||
{
|
||||
CSegment(CPoint(rect.fLeft, rect.fBottom), CPoint(rect.fRight, rect.fBottom)), // нижняя
|
||||
CSegment(CPoint(rect.fRight, rect.fBottom), CPoint(rect.fRight, rect.fTop)), // правая
|
||||
CSegment(CPoint(rect.fRight, rect.fTop), CPoint(rect.fLeft, rect.fTop)), // верхняя
|
||||
CSegment(CPoint(rect.fLeft, rect.fTop), CPoint(rect.fLeft, rect.fBottom)) // левая
|
||||
};
|
||||
|
||||
for (const auto& side : rectSides)
|
||||
{
|
||||
CPoint intersection;
|
||||
if (findLineIntersection(segment, side, intersection))
|
||||
{
|
||||
// Проверяем, что точка лежит на обоих отрезках
|
||||
if (pointOnSegment(intersection, segment) && pointOnSegment(intersection, side))
|
||||
intersections.push_back(intersection);
|
||||
}
|
||||
}
|
||||
|
||||
return intersections;
|
||||
}
|
||||
|
||||
// Нахождение пересечения двух отрезков
|
||||
bool findLineIntersection(const CSegment& seg1, const CSegment& seg2, CPoint& result)
|
||||
{
|
||||
double x1 = seg1.start.x, y1 = seg1.start.y;
|
||||
double x2 = seg1.end.x, y2 = seg1.end.y;
|
||||
double x3 = seg2.start.x, y3 = seg2.start.y;
|
||||
double x4 = seg2.end.x, y4 = seg2.end.y;
|
||||
|
||||
double denom = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
|
||||
|
||||
if (std::abs(denom) < 1e-9) return false; // Параллельные линии
|
||||
|
||||
double t = ((x1 - x3) * (y3 - y4) - (y1 - y3) * (x3 - x4)) / denom;
|
||||
double u = -((x1 - x2) * (y1 - y3) - (y1 - y2) * (x1 - x3)) / denom;
|
||||
|
||||
result.x = x1 + t * (x2 - x1);
|
||||
result.y = y1 + t * (y2 - y1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Проверка, лежит ли точка на отрезке
|
||||
bool pointOnSegment(const CPoint& p, const CSegment& seg)
|
||||
{
|
||||
// Проверяем коллинеарность и нахождение между концами
|
||||
return std::abs(distance(seg.start, p) + distance(p, seg.end) - distance(seg.start, seg.end)) < 1e-9;
|
||||
}
|
||||
|
||||
// Расстояние между двумя точками
|
||||
double distance(const CPoint& a, const CPoint& b)
|
||||
{
|
||||
return std::sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
|
||||
}
|
||||
|
||||
// Параметрическое значение точки на линии (0 = start, 1 = end)
|
||||
double getParametricValue(const CPoint& p, const CSegment& line)
|
||||
{
|
||||
double lineLength = distance(line.start, line.end);
|
||||
if (lineLength < 1e-9) return 0;
|
||||
|
||||
// Проекция на направляющий вектор линии
|
||||
double dx = line.end.x - line.start.x;
|
||||
double dy = line.end.y - line.start.y;
|
||||
|
||||
if (std::abs(dx) > std::abs(dy))
|
||||
return (p.x - line.start.x) / dx;
|
||||
else
|
||||
return (p.y - line.start.y) / dy;
|
||||
}
|
||||
|
||||
public:
|
||||
CLineClipper(const std::vector<TRect>& rects) : rectangles(rects) {}
|
||||
|
||||
// Основная функция для вычисления видимых сегментов
|
||||
std::vector<CSegment> getVisibleSegments(const CSegment& line)
|
||||
{
|
||||
std::vector<double> cutPoints = {0.0, 1.0}; // Начало и конец линии
|
||||
|
||||
// Собираем все точки пересечения со всеми прямоугольниками
|
||||
for (const auto& rect : rectangles)
|
||||
{
|
||||
if (intersectsRectangle(line, rect))
|
||||
{
|
||||
auto intersections = findIntersectionPoints(line, rect);
|
||||
for (const auto& p : intersections)
|
||||
{
|
||||
double t = getParametricValue(p, line);
|
||||
if (t >= 0 && t <= 1)
|
||||
cutPoints.push_back(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Сортируем точки по параметрическому значению
|
||||
std::sort(cutPoints.begin(), cutPoints.end());
|
||||
cutPoints.erase(std::unique(cutPoints.begin(), cutPoints.end()), cutPoints.end());
|
||||
|
||||
// Проверяем каждый сегмент на видимость
|
||||
std::vector<CSegment> visibleSegments;
|
||||
for (size_t i = 0; i < cutPoints.size() - 1; i++)
|
||||
{
|
||||
double t1 = cutPoints[i];
|
||||
double t2 = cutPoints[i + 1];
|
||||
|
||||
// Средняя точка сегмента для проверки видимости
|
||||
double t_mid = (t1 + t2) / 2.0;
|
||||
CPoint midPoint(
|
||||
line.start.x + t_mid * (line.end.x - line.start.x),
|
||||
line.start.y + t_mid * (line.end.y - line.start.y)
|
||||
);
|
||||
|
||||
// Сегмент видим, если его средняя точка не внутри ни одного прямоугольника
|
||||
if (!isPointInsideAnyRectangle(midPoint))
|
||||
{
|
||||
CPoint segStart(
|
||||
line.start.x + t1 * (line.end.x - line.start.x),
|
||||
line.start.y + t1 * (line.end.y - line.start.y)
|
||||
);
|
||||
CPoint segEnd(
|
||||
line.start.x + t2 * (line.end.x - line.start.x),
|
||||
line.start.y + t2 * (line.end.y - line.start.y)
|
||||
);
|
||||
visibleSegments.push_back(CSegment(segStart, segEnd));
|
||||
}
|
||||
}
|
||||
|
||||
return visibleSegments;
|
||||
}
|
||||
|
||||
private:
|
||||
// Проверка, находится ли точка внутри любого прямоугольника
|
||||
bool isPointInsideAnyRectangle(const CPoint& p)
|
||||
{
|
||||
for (const auto& rect : rectangles)
|
||||
{
|
||||
if (p.x >= rect.fLeft && p.x <= rect.fRight && p.y >= rect.fBottom && p.y <= rect.fTop)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
enum EGrMode
|
||||
{
|
||||
grmode_PAGE = 0x01,
|
||||
|
||||
Reference in New Issue
Block a user