mirror of
https://github.com/ONLYOFFICE/core.git
synced 2026-02-10 18:05:41 +08:00
Try DoPathStroke
This commit is contained in:
@ -160,9 +160,9 @@ CFile.prototype["UndoMergePages"] = function()
|
||||
{
|
||||
return this._UndoMergePages();
|
||||
};
|
||||
CFile.prototype["RedactPage"] = function(pageIndex, arrRedactBox, arrayBufferChanges)
|
||||
CFile.prototype["RedactPage"] = function(pageIndex, arrRedactBox, arrayBufferFiller)
|
||||
{
|
||||
return this._RedactPage(pageIndex, arrRedactBox, arrayBufferChanges);
|
||||
return this._RedactPage(pageIndex, arrRedactBox, arrayBufferFiller);
|
||||
};
|
||||
CFile.prototype["UndoRedact"] = function()
|
||||
{
|
||||
|
||||
@ -192,13 +192,13 @@ CFile.prototype._UndoMergePages = function()
|
||||
return Module["_UnmergePages"](this.nativeFile) == 1;
|
||||
};
|
||||
|
||||
CFile.prototype._RedactPage = function(pageIndex, arrRedactBox, arrayBufferChanges)
|
||||
CFile.prototype._RedactPage = function(pageIndex, arrRedactBox, arrayBufferFiller)
|
||||
{
|
||||
let changesPtr = 0;
|
||||
let changesLen = 0;
|
||||
if (arrayBufferChanges)
|
||||
if (arrayBufferFiller)
|
||||
{
|
||||
let changes = new Uint8Array(arrayBufferChanges);
|
||||
let changes = new Uint8Array(arrayBufferFiller);
|
||||
changesLen = changes.length;
|
||||
changesPtr = Module["_malloc"](changesLen);
|
||||
Module["HEAP8"].set(changes, changesPtr);
|
||||
|
||||
@ -191,6 +191,7 @@ SOURCES += \
|
||||
SrcWriter/ResourcesDictionary.cpp \
|
||||
SrcWriter/Shading.cpp \
|
||||
SrcWriter/Streams.cpp \
|
||||
SrcWriter/Types.cpp \
|
||||
SrcWriter/Utils.cpp \
|
||||
SrcWriter/Metadata.cpp \
|
||||
SrcWriter/States.cpp \
|
||||
|
||||
@ -302,13 +302,13 @@ void RedactOutputDev::updateTextShift(GfxState *pGState, double shift)
|
||||
//----- path painting
|
||||
void RedactOutputDev::stroke(GfxState *pGState)
|
||||
{
|
||||
DoPath(pGState, pGState->getPath(), pGState->getCTM(), NULL, true);
|
||||
DoPathStroke(pGState, pGState->getPath(), pGState->getCTM());
|
||||
DrawPath(c_nStroke);
|
||||
// TODO Нужно пересечь путь с областями Redact, результат записать и сделать stroke
|
||||
}
|
||||
void RedactOutputDev::fill(GfxState *pGState)
|
||||
{
|
||||
DoPath(pGState, pGState->getPath(), pGState->getCTM());
|
||||
DoPathFill(pGState, pGState->getPath(), pGState->getCTM());
|
||||
DrawPath(c_nWindingFillMode);
|
||||
// TODO Нужно пересечь путь с областями Redact, результат записать и сделать fill
|
||||
}
|
||||
@ -601,27 +601,15 @@ void ConvertConicToQuads(const pk::SkPoint& p0, const pk::SkPoint& p1, const pk:
|
||||
std::copy(left, left + 3, pts);
|
||||
std::copy(right + 1, right + 3, pts + 3);
|
||||
}
|
||||
void RedactOutputDev::DoPath(GfxState* pGState, GfxPath* pPath, double* pCTM, GfxClipMatrix* pCTM2, bool bStroke)
|
||||
void RedactOutputDev::DoPathFill(GfxState* pGState, GfxPath* pPath, double* pCTM)
|
||||
{
|
||||
double arrMatrix[6];
|
||||
if (pCTM2)
|
||||
{
|
||||
arrMatrix[0] = pCTM2->dA;
|
||||
arrMatrix[1] = pCTM2->dB;
|
||||
arrMatrix[2] = pCTM2->dC;
|
||||
arrMatrix[3] = pCTM2->dD;
|
||||
arrMatrix[4] = pCTM2->dE;
|
||||
arrMatrix[5] = pCTM2->dF;
|
||||
}
|
||||
else
|
||||
{
|
||||
arrMatrix[0] = pCTM[0];
|
||||
arrMatrix[1] = pCTM[1];
|
||||
arrMatrix[2] = pCTM[2];
|
||||
arrMatrix[3] = pCTM[3];
|
||||
arrMatrix[4] = pCTM[4];
|
||||
arrMatrix[5] = pCTM[5];
|
||||
}
|
||||
arrMatrix[0] = pCTM[0];
|
||||
arrMatrix[1] = pCTM[1];
|
||||
arrMatrix[2] = pCTM[2];
|
||||
arrMatrix[3] = pCTM[3];
|
||||
arrMatrix[4] = pCTM[4];
|
||||
arrMatrix[5] = pCTM[5];
|
||||
|
||||
double dShiftX = 0, dShiftY = 0;
|
||||
DoTransform(arrMatrix, &dShiftX, &dShiftY, true);
|
||||
@ -640,14 +628,6 @@ void RedactOutputDev::DoPath(GfxState* pGState, GfxPath* pPath, double* pCTM, Gf
|
||||
oPathRedact.LineTo(m_arrQuadPoints[i + 6], m_arrQuadPoints[i + 7]);
|
||||
oPathRedact.CloseFigure();
|
||||
}
|
||||
size_t length2 = oPathRedact.GetPointCount(), compound2 = oPathRedact.GetCloseCount();
|
||||
std::vector<Aggplus::PointD> points2 = oPathRedact.GetPoints(0, length2 + compound2);
|
||||
std::cout << "Path2:" <<std::endl;
|
||||
for (int i = 0; i < points2.size(); ++i)
|
||||
{
|
||||
std::cout << "( " << points2[i].X << ", " << points2[i].Y << " ); ";
|
||||
}
|
||||
std::cout <<std::endl;
|
||||
|
||||
for (int nSubPathIndex = 0, nSubPathCount = pPath->getNumSubpaths(); nSubPathIndex < nSubPathCount; ++nSubPathIndex)
|
||||
{
|
||||
@ -910,6 +890,158 @@ void RedactOutputDev::DoPath(GfxState* pGState, GfxPath* pPath, double* pCTM, Gf
|
||||
}
|
||||
}
|
||||
}
|
||||
void ToPath(CMatrix* pMatrix, const std::vector<CSegment>& oSegment, CPath* pPath, double& dXCur, double& dYCur)
|
||||
{
|
||||
for (int i = 0; i < oSegment.size(); ++i)
|
||||
{
|
||||
double dX = oSegment[i].start.x;
|
||||
double dY = oSegment[i].start.y;
|
||||
pMatrix->Apply(dX, dY);
|
||||
double dX2 = oSegment[i].end.x;
|
||||
double dY2 = oSegment[i].end.y;
|
||||
dXCur = dX, dYCur = dY;
|
||||
pMatrix->Apply(dX2, dY2);
|
||||
|
||||
pPath->MoveTo(dX, dY);
|
||||
pPath->LineTo(dX, dY);
|
||||
}
|
||||
}
|
||||
void ToPath(CMatrix* pMatrix, const std::vector<CCubicBezier>& oBezier, CPath* pPath, double& dXCur, double& dYCur)
|
||||
{
|
||||
for (int i = 0; i < oBezier.size(); ++i)
|
||||
{
|
||||
double dX = oBezier[i].p0.x;
|
||||
double dY = oBezier[i].p0.y;
|
||||
pMatrix->Apply(dX, dY);
|
||||
double dX1 = oBezier[i].p1.x;
|
||||
double dY1 = oBezier[i].p1.y;
|
||||
pMatrix->Apply(dX1, dY1);
|
||||
double dX2 = oBezier[i].p2.x;
|
||||
double dY2 = oBezier[i].p2.y;
|
||||
pMatrix->Apply(dX2, dY2);
|
||||
double dX3 = oBezier[i].p3.x;
|
||||
double dY3 = oBezier[i].p3.y;
|
||||
dXCur = dX3, dYCur = dY3;
|
||||
pMatrix->Apply(dX3, dY3);
|
||||
|
||||
pPath->MoveTo(dX, dY);
|
||||
pPath->CurveTo(dX1, dY1, dX2, dY2, dX3, dY3);
|
||||
}
|
||||
}
|
||||
void RedactOutputDev::DoPathStroke(GfxState* pGState, GfxPath* pPath, double* pCTM)
|
||||
{
|
||||
double arrMatrix[6];
|
||||
arrMatrix[0] = pCTM[0];
|
||||
arrMatrix[1] = pCTM[1];
|
||||
arrMatrix[2] = pCTM[2];
|
||||
arrMatrix[3] = pCTM[3];
|
||||
arrMatrix[4] = pCTM[4];
|
||||
arrMatrix[5] = pCTM[5];
|
||||
|
||||
double dShiftX = 0, dShiftY = 0;
|
||||
DoTransform(arrMatrix, &dShiftX, &dShiftY, true);
|
||||
|
||||
CMatrix oMatrix(m_arrMatrix[0], m_arrMatrix[1], m_arrMatrix[2], m_arrMatrix[3], m_arrMatrix[4], m_arrMatrix[5]);
|
||||
CMatrix oInverse = oMatrix.Inverse();
|
||||
|
||||
m_pRenderer->m_oPath.Clear();
|
||||
for (int nSubPathIndex = 0, nSubPathCount = pPath->getNumSubpaths(); nSubPathIndex < nSubPathCount; ++nSubPathIndex)
|
||||
{
|
||||
GfxSubpath* pSubpath = pPath->getSubpath(nSubPathIndex);
|
||||
int nPointsCount = pSubpath->getNumPoints();
|
||||
|
||||
double dX = pSubpath->getX(0), dY = pSubpath->getY(0);
|
||||
oMatrix.Apply(dX, dY);
|
||||
double dXCur = dX, dYCur = dY;
|
||||
double dXStart = dX, dYStart = dY;
|
||||
|
||||
int nCurPointIndex = 1;
|
||||
while (nCurPointIndex < nPointsCount)
|
||||
{
|
||||
if (pSubpath->getCurve(nCurPointIndex))
|
||||
{
|
||||
dX = pSubpath->getX(nCurPointIndex);
|
||||
dY = pSubpath->getY(nCurPointIndex);
|
||||
oMatrix.Apply(dX, dY);
|
||||
double dX2 = pSubpath->getX(nCurPointIndex + 1);
|
||||
double dY2 = pSubpath->getY(nCurPointIndex + 1);
|
||||
oMatrix.Apply(dX2, dY2);
|
||||
double dX3 = pSubpath->getX(nCurPointIndex + 2);
|
||||
double dY3 = pSubpath->getY(nCurPointIndex + 2);
|
||||
oMatrix.Apply(dX3, dY3);
|
||||
nCurPointIndex += 3;
|
||||
|
||||
std::vector<CCubicBezier> oResBezier = { CCubicBezier(CPoint(dXCur, dYCur), CPoint(dX, dY), CPoint(dX2, dY2), CPoint(dX3, dY3)) };
|
||||
|
||||
for (int i = 0; i < m_arrQuadPoints.size(); i += 4)
|
||||
{
|
||||
std::vector<CCubicBezier> oResBezier2;
|
||||
for (int iBezier = 0; iBezier < oResBezier.size(); ++iBezier)
|
||||
{
|
||||
std::vector<CCubicBezier> oResBezier3 = cutBezierWithRectangle(
|
||||
oResBezier[iBezier].p0.x, oResBezier[iBezier].p0.y, oResBezier[iBezier].p1.x, oResBezier[iBezier].p1.y,
|
||||
oResBezier[iBezier].p2.x, oResBezier[iBezier].p2.y, oResBezier[iBezier].p3.x, oResBezier[iBezier].p3.y,
|
||||
m_arrQuadPoints[i + 0], m_arrQuadPoints[i + 1], m_arrQuadPoints[i + 2], m_arrQuadPoints[i + 3]);
|
||||
oResBezier2.insert(oResBezier2.end(), oResBezier3.begin(), oResBezier3.end());
|
||||
}
|
||||
oResBezier = oResBezier2;
|
||||
}
|
||||
|
||||
if (oResBezier.empty())
|
||||
dXCur = dX3, dYCur = dY3;
|
||||
else
|
||||
ToPath(&oInverse, oResBezier, &m_pRenderer->m_oPath, dXCur, dYCur);
|
||||
}
|
||||
else
|
||||
{
|
||||
dX = pSubpath->getX(nCurPointIndex);
|
||||
dY = pSubpath->getY(nCurPointIndex);
|
||||
oMatrix.Apply(dX, dY);
|
||||
++nCurPointIndex;
|
||||
|
||||
std::vector<CSegment> oResSegment = { CSegment(CPoint(dXCur, dYCur), CPoint(dX, dY)) };
|
||||
|
||||
for (int i = 0; i < m_arrQuadPoints.size(); i += 4)
|
||||
{
|
||||
std::vector<CSegment> oResSegment2;
|
||||
for (int iSegment = 0; iSegment < oResSegment.size(); ++iSegment)
|
||||
{
|
||||
std::vector<CSegment> oResSegment3 = cutLineWithRectangle(
|
||||
oResSegment[iSegment].start.x, oResSegment[iSegment].start.y, oResSegment[iSegment].end.x, oResSegment[iSegment].end.y,
|
||||
m_arrQuadPoints[i + 0], m_arrQuadPoints[i + 1], m_arrQuadPoints[i + 2], m_arrQuadPoints[i + 3]);
|
||||
oResSegment2.insert(oResSegment2.end(), oResSegment3.begin(), oResSegment3.end());
|
||||
}
|
||||
oResSegment = oResSegment2;
|
||||
}
|
||||
|
||||
if (oResSegment.empty())
|
||||
dXCur = dX, dYCur = dY;
|
||||
else
|
||||
ToPath(&oInverse, oResSegment, &m_pRenderer->m_oPath, dXCur, dYCur);
|
||||
}
|
||||
}
|
||||
if (pSubpath->isClosed() && dXCur != dXStart && dYCur != dYStart)
|
||||
{
|
||||
std::vector<CSegment> oResSegment = { CSegment(CPoint(dXCur, dYCur), CPoint(dXStart, dYStart)) };
|
||||
|
||||
for (int i = 0; i < m_arrQuadPoints.size(); i += 4)
|
||||
{
|
||||
std::vector<CSegment> oResSegment2;
|
||||
for (int iSegment = 0; iSegment < oResSegment.size(); ++iSegment)
|
||||
{
|
||||
std::vector<CSegment> oResSegment3 = cutLineWithRectangle(
|
||||
oResSegment[iSegment].start.x, oResSegment[iSegment].start.y, oResSegment[iSegment].end.x, oResSegment[iSegment].end.y,
|
||||
m_arrQuadPoints[i + 0], m_arrQuadPoints[i + 1], m_arrQuadPoints[i + 2], m_arrQuadPoints[i + 3]);
|
||||
oResSegment2.insert(oResSegment2.end(), oResSegment3.begin(), oResSegment3.end());
|
||||
}
|
||||
oResSegment = oResSegment2;
|
||||
}
|
||||
|
||||
if (!oResSegment.empty())
|
||||
ToPath(&oInverse, oResSegment, &m_pRenderer->m_oPath, dXCur, dYCur);
|
||||
}
|
||||
}
|
||||
}
|
||||
void RedactOutputDev::DoTransform(double* pMatrix, double* pdShiftX, double* pdShiftY, bool bActual)
|
||||
{
|
||||
if (1 == pMatrix[0] && 0 == pMatrix[1] && 0 == pMatrix[2] && 1 == pMatrix[3] && !bActual)
|
||||
|
||||
@ -181,7 +181,8 @@ namespace PdfWriter
|
||||
std::vector<std::string> m_arrExtGState;
|
||||
};
|
||||
|
||||
void DoPath(GfxState* pGState, GfxPath* pPath, double* pCTM, GfxClipMatrix* pCTM2 = NULL, bool bStroke = false);
|
||||
void DoPathFill (GfxState* pGState, GfxPath* pPath, double* pCTM);
|
||||
void DoPathStroke(GfxState* pGState, GfxPath* pPath, double* pCTM);
|
||||
void DoTransform(double* pMatrix, double* pdShiftX, double* pdShiftY, bool bActual = false);
|
||||
void DrawPath(const LONG& lType);
|
||||
void UpdateTransform();
|
||||
|
||||
387
PdfFile/SrcWriter/Types.cpp
Normal file
387
PdfFile/SrcWriter/Types.cpp
Normal file
@ -0,0 +1,387 @@
|
||||
/*
|
||||
* (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 "Types.h"
|
||||
|
||||
namespace PdfWriter
|
||||
{
|
||||
|
||||
// Проверка пересечения отрезков и нахождение точки пересечения
|
||||
bool findIntersection(const CPoint& p1, const CPoint& p2, const CPoint& q1, const CPoint& q2, CPoint& intersection, double& t)
|
||||
{
|
||||
double det = (p2.x - p1.x) * (q2.y - q1.y) - (p2.y - p1.y) * (q2.x - q1.x);
|
||||
|
||||
if (std::abs(det) < EPS)
|
||||
return false; // Параллельны или совпадают
|
||||
|
||||
t = ((q1.x - p1.x) * (q2.y - q1.y) - (q1.y - p1.y) * (q2.x - q1.x)) / det;
|
||||
double u = ((q1.x - p1.x) * (p2.y - p1.y) - (q1.y - p1.y) * (p2.x - p1.x)) / det;
|
||||
|
||||
if (t >= -EPS && t <= 1.0 + EPS && u >= -EPS && u <= 1.0 + EPS)
|
||||
{
|
||||
intersection.x = p1.x + t * (p2.x - p1.x);
|
||||
intersection.y = p1.y + t * (p2.y - p1.y);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// Проверка, находится ли точка внутри прямоугольника
|
||||
bool isPointInsideRectangle(const CPoint& p, double dL, double dB, double dR, double dT)
|
||||
{
|
||||
return p.x >= dL - EPS && p.x <= dR + EPS &&
|
||||
p.y >= dB - EPS && p.y <= dT + EPS;
|
||||
}
|
||||
// Проверка, лежит ли прямая на стороне прямоугольника
|
||||
bool isLineOnRectangleSide(double dX1, double dY1, double dX2, double dY2, double dL, double dB, double dR, double dT)
|
||||
{
|
||||
// Проверяем совпадение с каждой стороной
|
||||
bool onBottom = std::abs(dY1 - dB) < EPS && std::abs(dY2 - dB) < EPS;
|
||||
bool onTop = std::abs(dY1 - dT) < EPS && std::abs(dY2 - dT) < EPS;
|
||||
bool onLeft = std::abs(dX1 - dL) < EPS && std::abs(dX2 - dL) < EPS;
|
||||
bool onRight = std::abs(dX1 - dR) < EPS && std::abs(dX2 - dR) < EPS;
|
||||
|
||||
if (onBottom || onTop)
|
||||
{
|
||||
double minX = std::min(dX1, dX2);
|
||||
double maxX = std::max(dX1, dX2);
|
||||
return minX <= dR + EPS && maxX >= dL - EPS;
|
||||
}
|
||||
|
||||
if (onLeft || onRight)
|
||||
{
|
||||
double minY = std::min(dY1, dY2);
|
||||
double maxY = std::max(dY1, dY2);
|
||||
return minY <= dT + EPS && maxY >= dB - EPS;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// Основная функция: разрезание прямой прямоугольником
|
||||
std::vector<CSegment> cutLineWithRectangle(double dX1, double dY1, double dX2, double dY2, double dL, double dB, double dR, double dT)
|
||||
{
|
||||
std::vector<CSegment> result;
|
||||
CPoint p1(dX1, dY1);
|
||||
CPoint p2(dX2, dY2);
|
||||
|
||||
// Проверяем особый случай - прямая лежит на стороне
|
||||
if (isLineOnRectangleSide(dX1, dY1, dX2, dY2, dL, dB, dR, dT))
|
||||
{
|
||||
// Вся прямая находится на границе прямоугольника
|
||||
return result; // Пустой результат - вся прямая "внутри"
|
||||
}
|
||||
|
||||
// Находим все точки пересечения с прямоугольником
|
||||
std::vector<CPoint> intersections;
|
||||
std::vector<double> tValues;
|
||||
|
||||
// Стороны прямоугольника
|
||||
CPoint sides[4][2] =
|
||||
{
|
||||
{ CPoint(dL, dB), CPoint(dR, dB) }, // нижняя
|
||||
{ CPoint(dR, dB), CPoint(dR, dT) }, // правая
|
||||
{ CPoint(dR, dT), CPoint(dL, dT) }, // верхняя
|
||||
{ CPoint(dL, dT), CPoint(dL, dB) } // левая
|
||||
};
|
||||
|
||||
double t;
|
||||
CPoint intersection;
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
if (findIntersection(p1, p2, sides[i][0], sides[i][1], intersection, t))
|
||||
{
|
||||
// Проверяем, нет ли уже такой точки
|
||||
bool exists = false;
|
||||
for (const auto& existing : intersections)
|
||||
{
|
||||
if (existing == intersection) {
|
||||
exists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!exists)
|
||||
{
|
||||
intersections.push_back(intersection);
|
||||
tValues.push_back(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Если нет пересечений
|
||||
if (intersections.empty())
|
||||
{
|
||||
// Проверяем, полностью ли прямая внутри или снаружи
|
||||
bool startInside = isPointInsideRectangle(p1, dL, dB, dR, dT);
|
||||
bool endInside = isPointInsideRectangle(p2, dL, dB, dR, dT);
|
||||
|
||||
if (startInside && endInside)
|
||||
{
|
||||
// Вся прямая внутри прямоугольника - возвращаем пустой вектор
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Вся прямая снаружи прямоугольника
|
||||
result.push_back(CSegment(p1, p2));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// Сортируем точки пересечения по параметру t
|
||||
for (size_t i = 0; i < tValues.size(); i++)
|
||||
{
|
||||
for (size_t j = i + 1; j < tValues.size(); j++)
|
||||
{
|
||||
if (tValues[j] < tValues[i])
|
||||
{
|
||||
std::swap(tValues[i], tValues[j]);
|
||||
std::swap(intersections[i], intersections[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Добавляем начальную и конечную точки для удобства
|
||||
std::vector<double> allT = { -EPS };
|
||||
std::vector<CPoint> allPoints = { p1 };
|
||||
|
||||
for (size_t i = 0; i < intersections.size(); i++)
|
||||
{
|
||||
allT.push_back(tValues[i]);
|
||||
allPoints.push_back(intersections[i]);
|
||||
}
|
||||
|
||||
allT.push_back(1.0 + EPS);
|
||||
allPoints.push_back(p2);
|
||||
|
||||
// Проходим по всем сегментам и оставляем те, которые снаружи прямоугольника
|
||||
for (size_t i = 0; i < allPoints.size() - 1; i++)
|
||||
{
|
||||
CPoint midPoint((allPoints[i].x + allPoints[i + 1].x) / 2.0,
|
||||
(allPoints[i].y + allPoints[i + 1].y) / 2.0);
|
||||
|
||||
if (!isPointInsideRectangle(midPoint, dL, dB, dR, dT))
|
||||
{
|
||||
// Этот сегмент снаружи прямоугольника
|
||||
result.push_back(CSegment(allPoints[i], allPoints[i + 1]));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
CPoint CCubicBezier::evaluate(double t) const
|
||||
{
|
||||
double t2 = t * t;
|
||||
double t3 = t2 * t;
|
||||
double u = 1 - t;
|
||||
double u2 = u * u;
|
||||
double u3 = u2 * u;
|
||||
|
||||
return p0 * u3 + p1 * (3 * u2 * t) + p2 * (3 * u * t2) + p3 * t3;
|
||||
}
|
||||
CCubicBezier CCubicBezier::split(double t) const
|
||||
{
|
||||
CPoint q0 = p0;
|
||||
CPoint q1 = p0 + (p1 - p0) * t;
|
||||
CPoint q2 = p1 + (p2 - p1) * t;
|
||||
CPoint q3 = p2 + (p3 - p2) * t;
|
||||
|
||||
CPoint r0 = q1 + (q2 - q1) * t;
|
||||
CPoint r1 = q2 + (q3 - q2) * t;
|
||||
CPoint r2 = r0 + (r1 - r0) * t;
|
||||
|
||||
return CCubicBezier(q0, q1, r0, r2);
|
||||
}
|
||||
CCubicBezier CCubicBezier::getSegment(double t0, double t1) const
|
||||
{
|
||||
if (std::abs(t0 - t1) < EPS)
|
||||
{
|
||||
CPoint pt = evaluate(t0);
|
||||
return CCubicBezier(pt, pt, pt, pt);
|
||||
}
|
||||
|
||||
// Разделяем кривую в точке t1
|
||||
CCubicBezier firstPart = split(t1);
|
||||
// Затем разделяем первую часть в точке t0/t1
|
||||
double normalizedT = t0 / t1;
|
||||
return firstPart.split(normalizedT);
|
||||
}
|
||||
|
||||
// Проверка пересечения отрезка с границей прямоугольника
|
||||
bool findLineRectangleIntersection(const CPoint& p1, const CPoint& p2, double dL, double dB, double dR, double dT, std::vector<double>& tValues)
|
||||
{
|
||||
bool found = false;
|
||||
|
||||
// Проверяем пересечение с каждой стороной
|
||||
auto checkSide = [&](double x1, double y1, double x2, double y2, int side)
|
||||
{
|
||||
double det = (p2.x - p1.x) * (y2 - y1) - (p2.y - p1.y) * (x2 - x1);
|
||||
if (std::abs(det) < EPS) return;
|
||||
|
||||
double t = ((x1 - p1.x) * (y2 - y1) - (y1 - p1.y) * (x2 - x1)) / det;
|
||||
double u = ((x1 - p1.x) * (p2.y - p1.y) - (y1 - p1.y) * (p2.x - p1.x)) / det;
|
||||
|
||||
if (t >= -EPS && t <= 1.0 + EPS && u >= -EPS && u <= 1.0 + EPS)
|
||||
{
|
||||
tValues.push_back(t);
|
||||
found = true;
|
||||
}
|
||||
};
|
||||
|
||||
// Нижняя сторона
|
||||
checkSide(dL, dB, dR, dB, 0);
|
||||
// Правая сторона
|
||||
checkSide(dR, dB, dR, dT, 1);
|
||||
// Верхняя сторона
|
||||
checkSide(dR, dT, dL, dT, 2);
|
||||
// Левая сторона
|
||||
checkSide(dL, dT, dL, dB, 3);
|
||||
|
||||
return found;
|
||||
}
|
||||
// Рекурсивное нахождение пересечений кривой с прямоугольником
|
||||
void findBezierRectangleIntersections(const CCubicBezier& bezier, double dL, double dB, double dR, double dT,
|
||||
double t0, double t1, std::vector<double>& intersections, int depth = 0)
|
||||
{
|
||||
if (depth > 20) return; // Ограничение глубины рекурсии
|
||||
|
||||
CPoint p0 = bezier.evaluate(t0);
|
||||
CPoint p1 = bezier.evaluate(t1);
|
||||
|
||||
// Проверяем, полностью ли сегмент внутри или снаружи
|
||||
bool inside0 = isPointInsideRectangle(p0, dL, dB, dR, dT);
|
||||
bool inside1 = isPointInsideRectangle(p1, dL, dB, dR, dT);
|
||||
|
||||
if (inside0 && inside1) {
|
||||
// Весь сегмент внутри - пересечений нет
|
||||
return;
|
||||
}
|
||||
|
||||
// Проверяем плоскую аппроксимацию (хорду)
|
||||
std::vector<double> chordIntersections;
|
||||
if (findLineRectangleIntersection(p0, p1, dL, dB, dR, dT, chordIntersections))
|
||||
{
|
||||
if (chordIntersections.size() == 1)
|
||||
{
|
||||
// Одно пересечение - добавляем его
|
||||
double t = t0 + chordIntersections[0] * (t1 - t0);
|
||||
intersections.push_back(t);
|
||||
}
|
||||
else if (chordIntersections.size() == 2)
|
||||
{
|
||||
// Два пересечения - добавляем оба
|
||||
double t1_val = t0 + chordIntersections[0] * (t1 - t0);
|
||||
double t2_val = t0 + chordIntersections[1] * (t1 - t0);
|
||||
intersections.push_back(t1_val);
|
||||
intersections.push_back(t2_val);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Если не нашли пересечений с хордой, но сегмент пересекает границу,
|
||||
// рекурсивно разделяем кривую
|
||||
double tMid = (t0 + t1) / 2.0;
|
||||
findBezierRectangleIntersections(bezier, dL, dB, dR, dT, t0, tMid, intersections, depth + 1);
|
||||
findBezierRectangleIntersections(bezier, dL, dB, dR, dT, tMid, t1, intersections, depth + 1);
|
||||
}
|
||||
// Быстрая проверка: полностью ли кривая внутри прямоугольника
|
||||
bool isBezierCompletelyInside(const CCubicBezier& bezier, const TRect& rect)
|
||||
{
|
||||
return isPointInsideRectangle(bezier.p0, rect.fLeft, rect.fBottom, rect.fRight, rect.fTop) &&
|
||||
isPointInsideRectangle(bezier.p1, rect.fLeft, rect.fBottom, rect.fRight, rect.fTop) &&
|
||||
isPointInsideRectangle(bezier.p2, rect.fLeft, rect.fBottom, rect.fRight, rect.fTop) &&
|
||||
isPointInsideRectangle(bezier.p3, rect.fLeft, rect.fBottom, rect.fRight, rect.fTop);
|
||||
}
|
||||
// Быстрая проверка: полностью ли кривая снаружи прямоугольника
|
||||
bool isBezierCompletelyOutside(const CCubicBezier& bezier, const TRect& rect)
|
||||
{
|
||||
// Проверяем по ограничивающему прямоугольнику кривой
|
||||
double minX = std::min({bezier.p0.x, bezier.p1.x, bezier.p2.x, bezier.p3.x});
|
||||
double maxX = std::max({bezier.p0.x, bezier.p1.x, bezier.p2.x, bezier.p3.x});
|
||||
double minY = std::min({bezier.p0.y, bezier.p1.y, bezier.p2.y, bezier.p3.y});
|
||||
double maxY = std::max({bezier.p0.y, bezier.p1.y, bezier.p2.y, bezier.p3.y});
|
||||
|
||||
return (maxX < rect.fLeft - EPS) || (minX > rect.fRight + EPS) ||
|
||||
(maxY < rect.fBottom - EPS) || (minY > rect.fTop + EPS);
|
||||
}
|
||||
// Основная функция для разрезания кривой Безье прямоугольником
|
||||
std::vector<CCubicBezier> cutBezierWithRectangle(
|
||||
double p0x, double p0y, double p1x, double p1y, double p2x, double p2y, double p3x, double p3y,
|
||||
double dL, double dB, double dR, double dT)
|
||||
{
|
||||
std::vector<CCubicBezier> result;
|
||||
CCubicBezier bezier(CPoint(p0x, p0y), CPoint(p1x, p1y), CPoint(p2x, p2y), CPoint(p3x, p3y));
|
||||
|
||||
// Быстрая проверка: если кривая полностью внутри или снаружи
|
||||
TRect rect(dL, dT, dR, dB);
|
||||
if (isBezierCompletelyInside(bezier, rect))
|
||||
{
|
||||
return result; // полностью внутри
|
||||
}
|
||||
if (isBezierCompletelyOutside(bezier, rect))
|
||||
{
|
||||
result.push_back(bezier);
|
||||
return result; // полностью снаружи
|
||||
}
|
||||
|
||||
// Находим все параметры t пересечения с прямоугольником
|
||||
std::vector<double> intersections;
|
||||
findBezierRectangleIntersections(bezier, dL, dB, dR, dT, 0.0, 1.0, intersections);
|
||||
|
||||
// Сортируем и удаляем дубликаты
|
||||
std::sort(intersections.begin(), intersections.end());
|
||||
intersections.erase(std::unique(intersections.begin(), intersections.end(),
|
||||
[](double a, double b) { return std::abs(a - b) < EPS; }), intersections.end());
|
||||
|
||||
// Добавляем граничные точки
|
||||
std::vector<double> allT = {0.0};
|
||||
allT.insert(allT.end(), intersections.begin(), intersections.end());
|
||||
allT.push_back(1.0);
|
||||
|
||||
// Создаем сегменты и проверяем, какие из них находятся снаружи
|
||||
for (size_t i = 0; i < allT.size() - 1; i++)
|
||||
{
|
||||
double t0 = allT[i];
|
||||
double t1 = allT[i + 1];
|
||||
double tMid = (t0 + t1) / 2.0;
|
||||
|
||||
CPoint midPoint = bezier.evaluate(tMid);
|
||||
|
||||
if (!isPointInsideRectangle(midPoint, dL, dB, dR, dT))
|
||||
{
|
||||
// Сегмент снаружи прямоугольника
|
||||
CCubicBezier segment = bezier.getSegment(t0, t1);
|
||||
result.push_back(segment);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@ -46,6 +46,7 @@
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include <math.h>
|
||||
#include <vector>
|
||||
|
||||
#ifdef __linux__
|
||||
#include <string.h>
|
||||
@ -53,6 +54,7 @@
|
||||
|
||||
namespace PdfWriter
|
||||
{
|
||||
const double EPS = 1e-10;
|
||||
struct TRect
|
||||
{
|
||||
TRect()
|
||||
@ -76,7 +78,7 @@ namespace PdfWriter
|
||||
double fTop;
|
||||
};
|
||||
typedef TRect TBox;
|
||||
class CMatrix
|
||||
class CMatrix
|
||||
{
|
||||
public:
|
||||
|
||||
@ -356,12 +358,17 @@ namespace PdfWriter
|
||||
class CPoint
|
||||
{
|
||||
public:
|
||||
|
||||
CPoint()
|
||||
{
|
||||
x = 0;
|
||||
y = 0;
|
||||
}
|
||||
CPoint(double dX, double dY) : x(dX), y(dY) {}
|
||||
CPoint(const CPoint& oPoint)
|
||||
{
|
||||
x = oPoint.x;
|
||||
y = oPoint.y;
|
||||
}
|
||||
void Set(double dX, double dY)
|
||||
{
|
||||
x = dX;
|
||||
@ -378,12 +385,50 @@ namespace PdfWriter
|
||||
y = oPoint.y;
|
||||
return *this;
|
||||
}
|
||||
bool operator==(const CPoint& oPoint) const
|
||||
{
|
||||
return std::abs(x - oPoint.x) < EPS && std::abs(y - oPoint.y) < EPS;
|
||||
}
|
||||
CPoint operator+(const CPoint& other) const
|
||||
{
|
||||
return CPoint(x + other.x, y + other.y);
|
||||
}
|
||||
CPoint operator-(const CPoint& other) const
|
||||
{
|
||||
return CPoint(x - other.x, y - other.y);
|
||||
}
|
||||
CPoint operator*(double scalar) const
|
||||
{
|
||||
return CPoint(x * scalar, y * scalar);
|
||||
}
|
||||
CPoint operator/(double scalar) const
|
||||
{
|
||||
return CPoint(x / scalar, y / scalar);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
double x;
|
||||
double y;
|
||||
};
|
||||
struct CSegment
|
||||
{
|
||||
CPoint start;
|
||||
CPoint end;
|
||||
CSegment(const CPoint& s, const CPoint& e) : start(s), end(e) {}
|
||||
};
|
||||
struct CCubicBezier
|
||||
{
|
||||
CPoint p0, p1, p2, p3;
|
||||
|
||||
CCubicBezier(const CPoint& p0, const CPoint& p1, const CPoint& p2, const CPoint& p3)
|
||||
: p0(p0), p1(p1), p2(p2), p3(p3) {}
|
||||
|
||||
CPoint evaluate(double t) const;
|
||||
// Разделение кривой в точке t
|
||||
CCubicBezier split(double t) const;
|
||||
// Получение сегмента кривой от t0 до t1
|
||||
CCubicBezier getSegment(double t0, double t1) const;
|
||||
};
|
||||
enum EGrMode
|
||||
{
|
||||
grmode_PAGE = 0x01,
|
||||
@ -464,6 +509,8 @@ namespace PdfWriter
|
||||
RenderingIntent_Saturation,
|
||||
RenderingIntent_Perceptual
|
||||
};
|
||||
std::vector<CSegment> cutLineWithRectangle(double dX1, double dY1, double dX2, double dY2, double dL, double dB, double dR, double dT);
|
||||
std::vector<CCubicBezier> cutBezierWithRectangle( double p0x, double p0y, double p1x, double p1y, double p2x, double p2y, double p3x, double p3y, double dL, double dB, double dR, double dT);
|
||||
}
|
||||
|
||||
#endif // _PDF_WRITER_SRC_TYPES_H
|
||||
|
||||
@ -482,9 +482,9 @@ TEST_F(CPdfFileTest, EditPdfFromBase64)
|
||||
RELEASEARRAYOBJECTS(pBuffer);
|
||||
RELEASEARRAYOBJECTS(pFileContent);
|
||||
|
||||
// pdfFile->Redact({ 136.145, 6.82016, 136.145, 293.838, 250.691, 293.838, 250.691, 6.82016 }); test1
|
||||
pdfFile->Redact({ 383.565, 343.134, 383.565, 773.827, 397.965, 773.827, 397.965, 343.134,
|
||||
88.364, 316.298, 88.364, 773.827, 214.037, 773.827, 214.037, 316.298 });
|
||||
// pdfFile->Redact({ 136.145, 6.82016, 250.691, 293.838 }); test1
|
||||
pdfFile->Redact({ 383.565, 343.134, 397.965, 773.827,
|
||||
88.364, 316.298, 214.037, 773.827 });
|
||||
|
||||
pdfFile->Close();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user