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

1197 lines
37 KiB
C++

/*
* (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 "RedactOutputDev.h"
#include "Types.h"
#include "Streams.h"
#include "Utils.h"
#include "../lib/xpdf/GfxFont.h"
#include "../lib/xpdf/XRef.h"
#include "../../DesktopEditor/graphics/GraphicsPath.h"
namespace PdfWriter
{
void Transform(double* pMatrix, double dUserX, double dUserY, double* pdDeviceX, double* pdDeviceY)
{
*pdDeviceX = dUserX * pMatrix[0] + dUserY * pMatrix[2] + pMatrix[4];
*pdDeviceY = dUserX * pMatrix[1] + dUserY * pMatrix[3] + pMatrix[5];
}
//----- constructor/destructor
RedactOutputDev::RedactOutputDev(CPdfWriter* pRenderer)
{
m_pXref = NULL;
m_pRenderer = pRenderer;
m_pDoc = m_pRenderer->GetDocument();
m_pPage = NULL;
m_bUpdateAll = false;
}
RedactOutputDev::~RedactOutputDev()
{
m_pRenderer = NULL;
m_pDoc = NULL;
m_pPage = NULL;
}
void RedactOutputDev::SetRedact(const std::vector<double>& arrQuadPoints)
{
m_arrQuadPoints = arrQuadPoints;
for (int i = 0; i < m_arrQuadPoints.size(); i += 8)
{
m_oPathRedact.StartFigure();
m_oPathRedact.MoveTo(m_arrQuadPoints[i + 0], m_arrQuadPoints[i + 1]);
m_oPathRedact.LineTo(m_arrQuadPoints[i + 2], m_arrQuadPoints[i + 3]);
m_oPathRedact.LineTo(m_arrQuadPoints[i + 4], m_arrQuadPoints[i + 5]);
m_oPathRedact.LineTo(m_arrQuadPoints[i + 6], m_arrQuadPoints[i + 7]);
m_oPathRedact.CloseFigure();
}
}
void RedactOutputDev::NewPDF(XRef* pXref)
{
m_pXref = pXref;
}
//----- initialization and control
void RedactOutputDev::setDefaultCTM(double *ctm)
{
memcpy(m_arrMatrix, ctm, 6 * sizeof(double));
if (m_pPage)
m_pPage->StartTransform(m_arrMatrix[0], m_arrMatrix[1], m_arrMatrix[2], m_arrMatrix[3], m_arrMatrix[4], m_arrMatrix[5]);
}
void RedactOutputDev::startPage(int nPageIndex, GfxState *pGState)
{
m_pPage = m_pDoc->GetEditPage(nPageIndex);
m_pRenderer->EditPage(m_pPage);
m_pDoc->SetCurPage(m_pPage);
m_pDoc->ClearPageFull();
}
void RedactOutputDev::endPage()
{
m_pRenderer->m_oCommandManager.Flush();
m_pPage = NULL;
}
//----- save/restore graphics state
void RedactOutputDev::saveState(GfxState *pGState)
{
m_pRenderer->m_oCommandManager.Flush();
DoStateOp();
m_pPage->GrSave();
m_sStates.push_back(GfxRedactState());
updateAll(pGState);
}
void RedactOutputDev::restoreState(GfxState *pGState)
{
m_pRenderer->m_oCommandManager.Flush();
m_pPage->GrRestore();
updateAll(pGState);
if (m_sStates.empty())
return; // Несбалансированный q/Q - сломанный файл
m_sStates.pop_back();
}
//----- update graphics state
void RedactOutputDev::updateAll(GfxState *pGState)
{
m_bUpdateAll = true;
updateLineDash(pGState);
updateFlatness(pGState);
updateLineJoin(pGState);
updateLineCap(pGState);
updateMiterLimit(pGState);
updateLineWidth(pGState);
// updateStrokeAdjust(pGState);
// updateFillColorSpace(pGState);
// updateFillColor(pGState);
// updateStrokeColorSpace(pGState);
// updateStrokeColor(pGState);
updateRenderingIntent(pGState);
// updateBlendMode(pGState);
// updateFillOpacity(pGState);
// updateStrokeOpacity(pGState);
updateFont(pGState);
updateCharSpace(pGState);
updateRender(pGState);
updateRise(pGState);
updateWordSpace(pGState);
updateHorizScaling(pGState);
m_bUpdateAll = false;
}
void RedactOutputDev::updateLineDash(GfxState *pGState)
{
double* pDash = NULL;
int nSize = 0;
double dStart = 0;
pGState->getLineDash(&pDash, &nSize, &dStart);
if (0 == nSize) // Solid
{
m_pRenderer->put_PenDashOffset(0);
m_pRenderer->put_PenDashStyle(Aggplus::DashStyleSolid);
}
else
{
m_pRenderer->m_oPen.SetDashPattern(pDash, nSize);
m_pRenderer->m_oPen.SetDashOffset(dStart);
m_pRenderer->m_oPen.SetDashStyle(Aggplus::DashStyleCustom);
}
if (m_bUpdateAll || m_sStates.empty())
return;
std::string sOp = "[ ";
for (int i = 0; i < nSize; ++i)
sOp += (std::to_string(pDash[i]) + " ");
sOp += "] " + std::to_string(dStart);
m_sStates.back().m_arrOp.push_back(std::make_pair(sOp, "d"));
}
void RedactOutputDev::updateFlatness(GfxState *pGState)
{
m_pRenderer->m_oPen.SetFlatness(pGState->getFlatness());
if (!m_bUpdateAll && !m_sStates.empty())
m_sStates.back().m_arrOp.push_back(std::make_pair(std::to_string(pGState->getFlatness()), "i"));
}
void RedactOutputDev::updateLineJoin(GfxState *pGState)
{
m_pRenderer->m_oPen.SetJoinStyle(pGState->getLineJoin());
if (!m_bUpdateAll && !m_sStates.empty())
m_sStates.back().m_arrOp.push_back(std::make_pair(std::to_string(pGState->getLineJoin()), "j"));
}
void RedactOutputDev::updateLineCap(GfxState *pGState)
{
m_pRenderer->m_oPen.SetStartCapStyle(pGState->getLineCap());
if (!m_bUpdateAll && !m_sStates.empty())
m_sStates.back().m_arrOp.push_back(std::make_pair(std::to_string(pGState->getLineCap()), "J"));
}
void RedactOutputDev::updateMiterLimit(GfxState *pGState)
{
m_pRenderer->m_oPen.SetMiter(pGState->getMiterLimit());
if (!m_bUpdateAll && !m_sStates.empty())
m_sStates.back().m_arrOp.push_back(std::make_pair(std::to_string(pGState->getMiterLimit()), "M"));
}
void RedactOutputDev::updateLineWidth(GfxState *pGState)
{
m_pRenderer->m_oPen.SetSize(pGState->getLineWidth());
if (!m_bUpdateAll && !m_sStates.empty())
m_sStates.back().m_arrOp.push_back(std::make_pair(std::to_string(pGState->getLineWidth()), "w"));
}
void RedactOutputDev::updateFillColor(GfxState *pGState)
{
if (m_sStates.empty())
return;
GfxColorSpace* pColorSpace = pGState->getFillColorSpace();
GfxColorSpaceMode eMode = pColorSpace->getMode();
GfxColor* pColor = pGState->getFillColor();
std::string sOp;
std::string sOp2;
switch (eMode)
{
case csDeviceGray:
{
sOp = std::to_string(colToDbl(pColor->c[0]));
sOp2 = "g";
break;
}
case csDeviceRGB:
{
sOp = std::to_string(colToDbl(pColor->c[0])) + " " + std::to_string(colToDbl(pColor->c[1])) + " " + std::to_string(colToDbl(pColor->c[2]));
sOp2 = "rg";
break;
}
case csDeviceCMYK:
{
sOp = std::to_string(colToDbl(pColor->c[0])) + " " + std::to_string(colToDbl(pColor->c[1])) + " " + std::to_string(colToDbl(pColor->c[2])) + " " + std::to_string(colToDbl(pColor->c[3]));
sOp2 = "k";
break;
}
default:
break;
}
m_sStates.back().m_arrOp.push_back(std::make_pair(sOp, sOp2));
}
void RedactOutputDev::updateStrokeColor(GfxState *pGState)
{
if (m_sStates.empty())
return;
GfxColorSpace* pColorSpace = pGState->getStrokeColorSpace();
GfxColorSpaceMode eMode = pColorSpace->getMode();
GfxColor* pColor = pGState->getStrokeColor();
std::string sOp;
std::string sOp2;
switch (eMode)
{
case csDeviceGray:
{
sOp = std::to_string(colToDbl(pColor->c[0]));
sOp2 = "G";
break;
}
case csDeviceRGB:
{
sOp = std::to_string(colToDbl(pColor->c[0])) + " " + std::to_string(colToDbl(pColor->c[1])) + " " + std::to_string(colToDbl(pColor->c[2]));
sOp2 = "RG";
break;
}
case csDeviceCMYK:
{
sOp = std::to_string(colToDbl(pColor->c[0])) + " " + std::to_string(colToDbl(pColor->c[1])) + " " + std::to_string(colToDbl(pColor->c[2])) + " " + std::to_string(colToDbl(pColor->c[3]));
sOp2 = "K";
break;
}
default:
break;
}
m_sStates.back().m_arrOp.push_back(std::make_pair(sOp, sOp2));
}
void RedactOutputDev::updateRenderingIntent(GfxState *pGState)
{
if (m_bUpdateAll || m_sStates.empty())
return;
std::string sOp = "/";
GfxRenderingIntent eRI = pGState->getRenderingIntent();
switch (eRI)
{
case GfxRenderingIntent::gfxRenderingIntentAbsoluteColorimetric:
sOp += "AbsoluteColorimetric";
break;
case GfxRenderingIntent::gfxRenderingIntentRelativeColorimetric:
sOp += "RelativeColorimetric";
break;
case GfxRenderingIntent::gfxRenderingIntentSaturation:
sOp += "Saturation";
break;
case GfxRenderingIntent::gfxRenderingIntentPerceptual:
default:
sOp += "Perceptual";
break;
}
m_sStates.back().m_arrOp.push_back(std::make_pair(sOp, "ri"));
}
//----- update text state
void RedactOutputDev::updateFont(GfxState *pGState)
{
GfxFont* pFont = pGState->getFont();
if (pFont)
{
m_pRenderer->m_oFont.SetSize(pGState->getFontSize());
m_pRenderer->put_FontName(NSFile::CUtf8Converter::GetUnicodeStringFromUTF8((BYTE*)pFont->getTag()->getCString(), pFont->getTag()->getLength()));
}
else
m_pRenderer->put_FontName(L"");
}
void RedactOutputDev::updateCharSpace(GfxState *pGState)
{
m_pRenderer->m_oFont.SetCharSpace(pGState->getCharSpace());
}
void RedactOutputDev::updateRender(GfxState *pGState)
{
int nRender = pGState->getRender();
m_pRenderer->m_oFont.SetRenderMode(nRender);
}
void RedactOutputDev::updateRise(GfxState *pGState)
{
m_pRenderer->m_oFont.SetRise(pGState->getRise());
}
void RedactOutputDev::updateWordSpace(GfxState *pGState)
{
m_pRenderer->m_oFont.SetWordSpace(pGState->getWordSpace());
}
void RedactOutputDev::updateHorizScaling(GfxState *pGState)
{
m_pRenderer->m_oFont.SetHorizontalScaling(pGState->getHorizScaling() * 100);
}
//----- path painting
void RedactOutputDev::stroke(GfxState *pGState)
{
DoPathRedact(pGState, pGState->getPath(), pGState->getCTM(), true);
DrawPath(c_nStroke);
}
void RedactOutputDev::fill(GfxState *pGState)
{
DoPathRedact(pGState, pGState->getPath(), pGState->getCTM());
DrawPath(c_nWindingFillMode);
}
void RedactOutputDev::eoFill(GfxState *pGState)
{
DoPathRedact(pGState, pGState->getPath(), pGState->getCTM(), false, true);
DrawPath(c_nEvenOddFillMode);
}
void RedactOutputDev::tilingPatternFill(GfxState *pGState, Gfx *gfx, Object *pStream, int nPaintType, int nTilingType, Dict *pResourcesDict,
double *pMatrix, double *pBBox, int nX0, int nY0, int nX1, int nY1, double dXStep, double dYStep)
{
}
//----- path clipping
void RedactOutputDev::clip(GfxState *pGState)
{
if (m_sStates.empty())
return;
if (!m_sStates.back().m_pClip)
m_sStates.back().m_pClip = new GfxClip();
int nClipFlag = c_nClipRegionIntersect | c_nClipRegionTypeWinding;
m_sStates.back().m_pClip->AddPath(pGState->getPath(), pGState->getCTM(), nClipFlag);
AddClip(pGState, &m_sStates.back(), m_sStates.back().m_pClip->GetPathNum() - 1);
}
void RedactOutputDev::eoClip(GfxState *pGState)
{
if (m_sStates.empty())
return;
if (!m_sStates.back().m_pClip)
m_sStates.back().m_pClip = new GfxClip();
int nClipFlag = c_nClipRegionIntersect | c_nClipRegionTypeEvenOdd;
m_sStates.back().m_pClip->AddPath(pGState->getPath(), pGState->getCTM(), nClipFlag);
AddClip(pGState, &m_sStates.back(), m_sStates.back().m_pClip->GetPathNum() - 1);
}
void RedactOutputDev::clipToStrokePath(GfxState *pGState)
{
if (m_sStates.empty())
return;
if (!m_sStates.back().m_pClip)
m_sStates.back().m_pClip = new GfxClip();
int nClipFlag = c_nClipRegionIntersect | c_nClipRegionTypeWinding | c_nClipToStrokePath;
m_sStates.back().m_pClip->AddPath(pGState->getPath(), pGState->getCTM(), nClipFlag);
AddClip(pGState, &m_sStates.back(), m_sStates.back().m_pClip->GetPathNum() - 1);
}
//----- text drawing
void RedactOutputDev::beginStringOp(GfxState *pGState)
{
m_pRenderer->m_oCommandManager.Flush();
DoStateOp();
}
void RedactOutputDev::drawChar(GfxState *pGState, double dX, double dY, double dDx, double dDy, double dOriginX, double dOriginY,
CharCode nCode, int nBytesCount, Unicode *pUnicode, int nUnicodeLen)
{
double* pCTM = pGState->getCTM();
double* pTm = pGState->getTextMat();
double pNewTm[6], arrMatrix[6];
double dTextScale = std::min(sqrt(pTm[2] * pTm[2] + pTm[3] * pTm[3]), sqrt(pTm[0] * pTm[0] + pTm[1] * pTm[1]));
double dITextScale = 1 / dTextScale;
double dOldSize = 10.0;
m_pRenderer->get_FontSize(&dOldSize);
if (dOldSize * dTextScale > 0)
{
m_pRenderer->put_FontSize(dOldSize * dTextScale);
pNewTm[0] = pTm[0] * dITextScale * pGState->getHorizScaling();
pNewTm[1] = pTm[1] * dITextScale * pGState->getHorizScaling();
pNewTm[2] = pTm[2] * dITextScale;
pNewTm[3] = pTm[3] * dITextScale;
pNewTm[4] = dX - dOriginX;
pNewTm[5] = dY - dOriginY;
}
else
{
m_pRenderer->put_FontSize(-dOldSize * dTextScale);
pNewTm[0] = pTm[0] * dITextScale * pGState->getHorizScaling();
pNewTm[1] = pTm[1] * dITextScale * pGState->getHorizScaling();
pNewTm[2] = pTm[2] * dITextScale;
pNewTm[3] = pTm[3] * dITextScale;
pNewTm[4] = dX;
pNewTm[5] = dY;
}
arrMatrix[0] = pNewTm[0] * pCTM[0] + pNewTm[1] * pCTM[2];
arrMatrix[1] = pNewTm[0] * pCTM[1] + pNewTm[1] * pCTM[3];
arrMatrix[2] = pNewTm[2] * pCTM[0] + pNewTm[3] * pCTM[2];
arrMatrix[3] = pNewTm[2] * pCTM[1] + pNewTm[3] * pCTM[3];
arrMatrix[4] = pNewTm[4] * pCTM[0] + pNewTm[5] * pCTM[2] + pCTM[4];
arrMatrix[5] = pNewTm[4] * pCTM[1] + pNewTm[5] * pCTM[3] + pCTM[5];
double dSize = 1;
if (true)
{
double dNorma = std::min(sqrt(arrMatrix[0] * arrMatrix[0] + arrMatrix[1] * arrMatrix[1]), sqrt(arrMatrix[2] * arrMatrix[2] + arrMatrix[3] * arrMatrix[3]));
if (dNorma > 0 && dNorma != 1)
{
arrMatrix[0] /= dNorma;
arrMatrix[1] /= dNorma;
arrMatrix[2] /= dNorma;
arrMatrix[3] /= dNorma;
m_pRenderer->get_FontSize(&dSize);
dSize *= dNorma;
m_pRenderer->put_FontSize(dSize);
}
}
double dShiftX = 0, dShiftY = 0;
DoTransform(arrMatrix, &dShiftX, &dShiftY, true);
double startX, startY, endX, endY;
pGState->transform(dX, dY, &startX, &startY);
pGState->transform(dX + dDx, dY + dDy, &endX, &endY);
double dCenterX = (startX + endX) / 2;
double dCenterY = (startY + endY) / 2;
for (int i = 0; i < m_arrQuadPoints.size(); i += 8)
{
double x1 = m_arrQuadPoints[i + 0];
double y1 = m_arrQuadPoints[i + 1];
double x2 = m_arrQuadPoints[i + 2];
double y2 = m_arrQuadPoints[i + 3];
double x3 = m_arrQuadPoints[i + 4];
double y3 = m_arrQuadPoints[i + 5];
double x4 = m_arrQuadPoints[i + 6];
double y4 = m_arrQuadPoints[i + 7];
if (isPointInQuad(dCenterX, dCenterY, x1, y1, x2, y2, x3, y3, x4, y4))
{
m_pRenderer->put_FontSize(dOldSize);
return;
}
}
BYTE* pCodes = new BYTE[2];
pCodes[0] = (nCode >> 8) & 0xFF;
pCodes[1] = nCode & 0xFF;
m_pRenderer->m_oCommandManager.SetTransform(m_arrMatrix[0], m_arrMatrix[1], m_arrMatrix[2], m_arrMatrix[3], m_arrMatrix[4], m_arrMatrix[5]);
CRendererTextCommand* pText = m_pRenderer->m_oCommandManager.AddText(pCodes, 2, dShiftX, dShiftY);
pText->SetName(m_pRenderer->m_oFont.GetName());
pText->SetSize(m_pRenderer->m_oFont.GetSize());
pText->SetType((EFontType)pGState->getFont()->getType());
pText->SetCharSpace(m_pRenderer->m_oFont.GetCharSpace());
pText->SetMode(m_pRenderer->m_oFont.GetRenderMode());
pText->SetRise(m_pRenderer->m_oFont.GetRise());
pText->SetWordSpace(m_pRenderer->m_oFont.GetWordSpace());
pText->SetHorScaling(m_pRenderer->m_oFont.GetHorizontalScaling());
pText->SetWidth(dDx);
m_pRenderer->put_FontSize(dOldSize);
}
//----- additional
GBool RedactOutputDev::useNameOp()
{
return gTrue;
}
void RedactOutputDev::setExtGState(const char* name)
{
if (m_sStates.empty())
return;
std::string sOp = "/" + std::string(name);
m_sStates.back().m_arrOp.push_back(std::make_pair(sOp, "gs"));
}
void RedactOutputDev::setFillColorSpace(const char* name)
{
if (m_sStates.empty())
return;
std::string sOp = "/" + std::string(name);
m_sStates.back().m_arrOp.push_back(std::make_pair(sOp, "cs"));
}
void RedactOutputDev::setFillColor(Object* args, int numArgs)
{
if (m_sStates.empty())
return;
std::string sOp;
for (int i = 0; i < numArgs; ++i)
{
if (args[i].isName())
sOp += ("/" + std::string(args[i].getName()) + " ");
else if (args[i].isInt())
sOp += (std::to_string(args[i].getInt()) + " ");
else if (args[i].isReal())
sOp += (std::to_string(args[i].getReal()) + " ");
}
m_sStates.back().m_arrOp.push_back(std::make_pair(sOp, "sc"));
}
void RedactOutputDev::setFillColorN(Object* args, int numArgs)
{
if (m_sStates.empty())
return;
std::string sOp;
for (int i = 0; i < numArgs; ++i)
{
if (args[i].isName())
sOp += ("/" + std::string(args[i].getName()) + " ");
else if (args[i].isInt())
sOp += (std::to_string(args[i].getInt()) + " ");
else if (args[i].isReal())
sOp += (std::to_string(args[i].getReal()) + " ");
}
m_sStates.back().m_arrOp.push_back(std::make_pair(sOp, "scn"));
}
void RedactOutputDev::setStrokeColorSpace(const char* name)
{
if (m_sStates.empty())
return;
std::string sOp = "/" + std::string(name);
m_sStates.back().m_arrOp.push_back(std::make_pair(sOp, "CS"));
}
void RedactOutputDev::setStrokeColor(Object* args, int numArgs)
{
if (m_sStates.empty())
return;
std::string sOp;
for (int i = 0; i < numArgs; ++i)
{
if (args[i].isName())
sOp += ("/" + std::string(args[i].getName()) + " ");
else if (args[i].isInt())
sOp += (std::to_string(args[i].getInt()) + " ");
else if (args[i].isReal())
sOp += (std::to_string(args[i].getReal()) + " ");
}
m_sStates.back().m_arrOp.push_back(std::make_pair(sOp, "SC"));
}
void RedactOutputDev::setStrokeColorN(Object* args, int numArgs)
{
if (m_sStates.empty())
return;
std::string sOp;
for (int i = 0; i < numArgs; ++i)
{
if (args[i].isName())
sOp += ("/" + std::string(args[i].getName()) + " ");
else if (args[i].isInt())
sOp += (std::to_string(args[i].getInt()) + " ");
else if (args[i].isReal())
sOp += (std::to_string(args[i].getReal()) + " ");
}
m_sStates.back().m_arrOp.push_back(std::make_pair(sOp, "SCN"));
}
void RedactOutputDev::setShading(GfxState *pGState, const char* name)
{
m_pRenderer->m_oCommandManager.Flush();
DoStateOp();
double dShiftX = 0, dShiftY = 0;
DoTransform(pGState->getCTM(), &dShiftX, &dShiftY, true);
// TODO Нужно проверять Shading на отсечение?
m_pPage->GrSave();
UpdateTransform();
DoStateOp();
CStream* pStream = m_pPage->GetStream();
pStream->WriteEscapeName(name);
pStream->WriteChar(' ');
pStream->WriteStr("sh");
pStream->WriteStr("\012");
m_pPage->GrRestore();
}
//----- image drawing
void RedactOutputDev::drawImageMask(GfxState *pGState, Object *pRef, Stream *pStream, int nWidth, int nHeight, GBool bInvert, GBool bInlineImage, GBool interpolate)
{
}
void RedactOutputDev::setSoftMaskFromImageMask(GfxState *pGState, Object *pRef, Stream *pStream, int nWidth, int nHeight, GBool bInvert, GBool bInlineImage, GBool interpolate)
{
}
void RedactOutputDev::drawImage(GfxState *pGState, Object *pRef, Stream *pStream, int nWidth, int nHeight, GfxImageColorMap *pColorMap, int *pMaskColors, GBool bInlineImg, GBool interpolate)
{
}
void RedactOutputDev::drawMaskedImage(GfxState *pGState, Object *pRef, Stream *pStream, int nWidth, int nHeight, GfxImageColorMap *pColorMap,
Object* pMaskRef, Stream *pMaskStream, int nMaskWidth, int nMaskHeight, GBool bMaskInvert, GBool interpolate)
{
}
void RedactOutputDev::drawSoftMaskedImage(GfxState *pGState, Object *pRef, Stream *pStream, int nWidth, int nHeight, GfxImageColorMap *pColorMap,
Object *maskRef, Stream *pMaskStream, int nMaskWidth, int nMaskHeight, GfxImageColorMap *pMaskColorMap, double *pMatte, GBool interpolate)
{
}
//----- Type 3 font operators
void RedactOutputDev::type3D0(GfxState *pGState, double wx, double wy)
{
}
void RedactOutputDev::type3D1(GfxState *pGState, double wx, double wy, double llx, double lly, double urx, double ury)
{
}
//----- form XObjects
void RedactOutputDev::drawForm(GfxState *pGState, Ref id, const char* name)
{
m_pRenderer->m_oCommandManager.Flush();
DoStateOp();
double dShiftX = 0, dShiftY = 0;
DoTransform(pGState->getCTM(), &dShiftX, &dShiftY, true);
// TODO пока что исключается всё изображение
Object oForm;
if (!m_pXref->fetch(id.num, id.gen, &oForm)->isStream())
{
oForm.free();
return;
}
double dXmin = 0, dYmin = 0, dXmax = 0, dYmax = 0;
double dX1 = 0, dY1 = 0, dX2 = 0, dY2 = 1, dX3 = 1, dY3 = 1, dX4 = 1, dY4 = 0;
Object oObj;
if (oForm.streamGetDict()->lookup("BBox", &oObj)->isArray() && oObj.arrayGetLength() == 4)
{
Object oNum;
if (oObj.arrayGet(0, &oNum)->isNum())
dXmin = oNum.getNum();
oNum.free();
if (oObj.arrayGet(1, &oNum)->isNum())
dYmin = oNum.getNum();
oNum.free();
if (oObj.arrayGet(2, &oNum)->isNum())
dXmax = oNum.getNum();
oNum.free();
if (oObj.arrayGet(3, &oNum)->isNum())
dYmax = oNum.getNum();
oNum.free(); oObj.free();
dX1 = dXmin, dY1 = dYmin;
dX2 = dXmin, dY2 = dYmax;
dX3 = dXmax, dY3 = dYmax;
dX4 = dXmax, dY4 = dYmin;
double oMatrix[6] = { 1, 0, 0, 1, 0, 0 };
if (oForm.streamGetDict()->lookup("Matrix", &oObj)->isArray() && oObj.arrayGetLength() == 6)
{
Object oObj2;
for (int i = 0; i < 6; ++i)
{
oMatrix[i] = oObj.arrayGet(i, &oObj2)->getNum();
oObj2.free();
}
}
Transform(oMatrix, dX1, dY1, &dX1, &dY1);
Transform(oMatrix, dX2, dY2, &dX2, &dY2);
Transform(oMatrix, dX3, dY3, &dX3, &dY3);
Transform(oMatrix, dX4, dY4, &dX4, &dY4);
Transform(m_arrMatrix, dX1, dY1, &dX1, &dY1);
Transform(m_arrMatrix, dX2, dY2, &dX2, &dY2);
Transform(m_arrMatrix, dX3, dY3, &dX3, &dY3);
Transform(m_arrMatrix, dX4, dY4, &dX4, &dY4);
}
oObj.free();
std::vector<CPoint> poly2 =
{
CPoint(dX1, dY1),
CPoint(dX2, dY2),
CPoint(dX3, dY3),
CPoint(dX4, dY4)
};
for (int i = 0; i < m_arrQuadPoints.size(); i += 8)
{
std::vector<CPoint> poly1 =
{
CPoint(m_arrQuadPoints[i + 0], m_arrQuadPoints[i + 1]),
CPoint(m_arrQuadPoints[i + 2], m_arrQuadPoints[i + 3]),
CPoint(m_arrQuadPoints[i + 4], m_arrQuadPoints[i + 5]),
CPoint(m_arrQuadPoints[i + 6], m_arrQuadPoints[i + 7])
};
if (PdfWriter::SAT(poly1, poly2))
return;
}
m_pPage->GrSave();
UpdateTransform();
m_pPage->ExecuteXObject(name);
m_pPage->GrRestore();
}
void RedactOutputDev::drawImage(GfxState *pGState, Ref id, const char* name)
{
m_pRenderer->m_oCommandManager.Flush();
DoStateOp();
double dShiftX = 0, dShiftY = 0;
DoTransform(pGState->getCTM(), &dShiftX, &dShiftY, true);
// TODO пока что исключается всё изображение
double dX1 = 0, dY1 = 0, dX2 = 0, dY2 = 1, dX3 = 1, dY3 = 1, dX4 = 1, dY4 = 0;
Transform(m_arrMatrix, dX1, dY1, &dX1, &dY1);
Transform(m_arrMatrix, dX2, dY2, &dX2, &dY2);
Transform(m_arrMatrix, dX3, dY3, &dX3, &dY3);
Transform(m_arrMatrix, dX4, dY4, &dX4, &dY4);
std::vector<CPoint> poly2 =
{
CPoint(dX1, dY1),
CPoint(dX2, dY2),
CPoint(dX3, dY3),
CPoint(dX4, dY4)
};
for (int j = 0; j < m_arrQuadPoints.size(); j += 8)
{
std::vector<CPoint> poly1 =
{
CPoint(m_arrQuadPoints[j + 0], m_arrQuadPoints[j + 1]),
CPoint(m_arrQuadPoints[j + 2], m_arrQuadPoints[j + 3]),
CPoint(m_arrQuadPoints[j + 4], m_arrQuadPoints[j + 5]),
CPoint(m_arrQuadPoints[j + 6], m_arrQuadPoints[j + 7])
};
if (PdfWriter::SAT(poly1, poly2))
return;
}
m_pPage->GrSave();
UpdateTransform();
m_pPage->ExecuteXObject(name);
m_pPage->GrRestore();
}
//----- transparency groups and soft masks
void RedactOutputDev::beginTransparencyGroup(GfxState *pGState, double *pBBox, GfxColorSpace *pBlendingColorSpace, GBool bIsolated, GBool bKnockout, GBool bForSoftMask)
{
}
void RedactOutputDev::endTransparencyGroup(GfxState *pGState)
{
}
void RedactOutputDev::paintTransparencyGroup(GfxState *pGState, double *pBBox)
{
}
void RedactOutputDev::setSoftMask(GfxState *pGState, double *pBBox, GBool bAlpha, Function *pTransferFunc, GfxColor *pBackdropColor)
{
}
void RedactOutputDev::clearSoftMask(GfxState *pGState)
{
}
bool SkipPath(const std::vector<CSegment>& arrForStroke, const CPoint& P1, const CPoint& P2)
{
for (int i = 0; i < arrForStroke.size(); ++i)
{
CPoint P3 = arrForStroke[i].start;
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 RedactOutputDev::DrawPathRedact(Aggplus::CGraphicsPath* oPath, bool bStroke, const std::vector<CSegment>& arrForStroke)
{
CMatrix oMatrix(m_arrMatrix[0], m_arrMatrix[1], m_arrMatrix[2], m_arrMatrix[3], m_arrMatrix[4], m_arrMatrix[5]);
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);
m_pRenderer->m_oPath.MoveTo(dXCI, dYCI);
}
dXCur = dX3; dYCur = dY3;
oInverse.Apply(dX3, dY3);
m_pRenderer->m_oPath.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);
m_pRenderer->m_oPath.MoveTo(dX, dY);
}
}
else if (oPath->IsLinePoint(i))
{
double dX = points[i].X, dY = points[i].Y;
if (bStroke && SkipPath(arrForStroke, CPoint(dXCur, dYCur), CPoint(dX, dY)))
{
dXCur = dX; dYCur = dY;
bBreak = true;
continue;
}
if (bBreak)
{
bBreak = false;
double dXCI = dXCur, dYCI = dYCur;
oInverse.Apply(dXCI, dYCI);
m_pRenderer->m_oPath.MoveTo(dXCI, dYCI);
}
dXCur = dX; dYCur = dY;
oInverse.Apply(dX, dY);
m_pRenderer->m_oPath.LineTo(dX, dY);
}
else if (oPath->IsClosePoint(i))
{
if (bStroke && (std::abs(dXCur - dXStart) > EPS || std::abs(dYCur - dYStart) > EPS) && SkipPath(arrForStroke, CPoint(dXCur, dYCur), CPoint(dXStart, dYStart)))
{
dXCur = dXStart; dYCur = dYStart;
bBreak = true;
continue;
}
if (bStroke || bBreak)
{
if (std::abs(dXCur - dXStart) > EPS || std::abs(dYCur - dYStart) > EPS)
{
bBreak = false;
double dXCI = dXCur, dYCI = dYCur;
oInverse.Apply(dXCI, dYCI);
double dXSI = dXStart, dYSI = dYStart;
oInverse.Apply(dXSI, dYSI);
m_pRenderer->m_oPath.MoveTo(dXCI, dYCI);
m_pRenderer->m_oPath.LineTo(dXSI, dYSI);
}
}
else
m_pRenderer->m_oPath.Close();
}
}
}
void RedactOutputDev::DoPathRedact(GfxState* pGState, GfxPath* pPath, double* pCTM, bool bStroke, bool bEoFill)
{
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);
m_pRenderer->m_oPath.Clear();
CMatrix oMatrix(m_arrMatrix[0], m_arrMatrix[1], m_arrMatrix[2], m_arrMatrix[3], m_arrMatrix[4], m_arrMatrix[5]);
CMatrix oInverse = oMatrix.Inverse();
std::vector<CSegment> arrForStroke;
Aggplus::CGraphicsPath oPath, oPathResult;
if (bEoFill)
oPath.SetRuler(true);
if (bStroke)
{
std::vector<std::vector<CPoint>> rectangles;
for (int i = 0; i < m_arrQuadPoints.size(); i += 8)
{
arrForStroke.push_back(CSegment(CPoint(m_arrQuadPoints[i + 0], m_arrQuadPoints[i + 1]), CPoint(m_arrQuadPoints[i + 2], m_arrQuadPoints[i + 3])));
arrForStroke.push_back(CSegment(CPoint(m_arrQuadPoints[i + 2], m_arrQuadPoints[i + 3]), CPoint(m_arrQuadPoints[i + 4], m_arrQuadPoints[i + 5])));
arrForStroke.push_back(CSegment(CPoint(m_arrQuadPoints[i + 4], m_arrQuadPoints[i + 5]), CPoint(m_arrQuadPoints[i + 6], m_arrQuadPoints[i + 7])));
arrForStroke.push_back(CSegment(CPoint(m_arrQuadPoints[i + 6], m_arrQuadPoints[i + 7]), CPoint(m_arrQuadPoints[i + 0], m_arrQuadPoints[i + 1])));
std::vector<CPoint> rectangle = { CPoint(m_arrQuadPoints[i + 0], m_arrQuadPoints[i + 1]), CPoint(m_arrQuadPoints[i + 2], m_arrQuadPoints[i + 3]),
CPoint(m_arrQuadPoints[i + 4], m_arrQuadPoints[i + 5]), CPoint(m_arrQuadPoints[i + 6], m_arrQuadPoints[i + 7]) };
rectangles.push_back(rectangle);
}
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 dXStart = dX, dYStart = dY, dXCur = dX, dYCur = 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;
oPath.StartFigure();
oPath.MoveTo(dXCur, dYCur);
oPath.CurveTo(dX, dY, dX2, dY2, dX3, dY3);
oPath.CloseFigure();
arrForStroke.push_back(CSegment(CPoint(dXCur, dYCur), CPoint(dX3, dY3)));
dXCur = dX3, dYCur = dY3;
oPathResult = Aggplus::CalcBooleanOperation(oPath, m_oPathRedact, Aggplus::BooleanOpType::Subtraction);
DrawPathRedact(&oPathResult, bStroke, arrForStroke);
oPathResult.Reset(); oPath.Reset();
}
else
{
dX = pSubpath->getX(nCurPointIndex);
dY = pSubpath->getY(nCurPointIndex);
oMatrix.Apply(dX, dY);
++nCurPointIndex;
CSegment line(CPoint(dXCur, dYCur), CPoint(dX, dY));
dXCur = dX; dYCur = dY;
auto visibleSegments = RectangleIntersection::findSegmentsOutsideRectangles(line, rectangles);
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);
m_pRenderer->m_oPath.MoveTo(dX1, dY1);
oInverse.Apply(dX2, dY2);
m_pRenderer->m_oPath.LineTo(dX2, dY2);
}
}
}
if (pSubpath->isClosed())
{
CSegment line(CPoint(dXCur, dYCur), CPoint(dXStart, dYStart));
auto visibleSegments = RectangleIntersection::findSegmentsOutsideRectangles(line, rectangles);
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);
m_pRenderer->m_oPath.MoveTo(dX1, dY1);
oInverse.Apply(dX2, dY2);
m_pRenderer->m_oPath.LineTo(dX2, dY2);
}
}
}
}
else
{
for (int nSubPathIndex = 0, nSubPathCount = pPath->getNumSubpaths(); nSubPathIndex < nSubPathCount; ++nSubPathIndex)
{
GfxSubpath* pSubpath = pPath->getSubpath(nSubPathIndex);
int nPointsCount = pSubpath->getNumPoints();
oPath.StartFigure();
double dX = pSubpath->getX(0), dY = pSubpath->getY(0);
oMatrix.Apply(dX, dY);
oPath.MoveTo(dX, 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);
oPath.CurveTo(dX, dY, dX2, dY2, dX3, dY3);
nCurPointIndex += 3;
}
else
{
dX = pSubpath->getX(nCurPointIndex);
dY = pSubpath->getY(nCurPointIndex);
oMatrix.Apply(dX, dY);
oPath.LineTo(dX, dY);
++nCurPointIndex;
}
}
// if (pSubpath->isClosed()) Принудительное замыкание фигур для CGraphicsPath
oPath.CloseFigure();
}
oPathResult = Aggplus::CalcBooleanOperation(oPath, m_oPathRedact, Aggplus::BooleanOpType::Subtraction);
DrawPathRedact(&oPathResult, bStroke);
}
}
void RedactOutputDev::DoPath(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);
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);
m_pRenderer->m_oPath.MoveTo(dX, dY);
int nCurPointIndex = 1;
while (nCurPointIndex < nPointsCount)
{
if (pSubpath->getCurve(nCurPointIndex))
{
dX = pSubpath->getX(nCurPointIndex);
dY = pSubpath->getY(nCurPointIndex);
double dX2 = pSubpath->getX(nCurPointIndex + 1);
double dY2 = pSubpath->getY(nCurPointIndex + 1);
double dX3 = pSubpath->getX(nCurPointIndex + 2);
double dY3 = pSubpath->getY(nCurPointIndex + 2);
m_pRenderer->m_oPath.CurveTo(dX, dY, dX2, dY2, dX3, dY3);
nCurPointIndex += 3;
}
else
{
dX = pSubpath->getX(nCurPointIndex);
dY = pSubpath->getY(nCurPointIndex);
m_pRenderer->m_oPath.LineTo(dX, dY);
++nCurPointIndex;
}
}
if (pSubpath->isClosed())
m_pRenderer->m_oPath.Close();
}
}
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)
{
if (pMatrix[4] || pMatrix[5])
{
*pdShiftX = pMatrix[4];
*pdShiftY = pMatrix[5];
}
m_pRenderer->ResetTransform();
m_arrMatrix[0] = 1; m_arrMatrix[1] = 0;
m_arrMatrix[2] = 0; m_arrMatrix[3] = 1;
m_arrMatrix[4] = 0; m_arrMatrix[5] = 0;
}
else if (m_arrMatrix[0] == pMatrix[0] && m_arrMatrix[1] == pMatrix[1] && m_arrMatrix[2] == pMatrix[2] && m_arrMatrix[3] == pMatrix[3]
&& m_arrMatrix[4] == pMatrix[4] && m_arrMatrix[5] == pMatrix[5] && !bActual)
{
double dIDet = 1 / (pMatrix[0] * pMatrix[3] - pMatrix[1] * pMatrix[2]);
*pdShiftX = ((pMatrix[4] - m_arrMatrix[4]) * m_arrMatrix[3] - (pMatrix[5] - m_arrMatrix[5]) * m_arrMatrix[1]) * dIDet;
*pdShiftY = ((pMatrix[5] - m_arrMatrix[5]) * m_arrMatrix[0] - (pMatrix[4] - m_arrMatrix[4]) * m_arrMatrix[2]) * dIDet;
}
else
{
m_pRenderer->SetTransform(pMatrix[0], pMatrix[1], pMatrix[2], pMatrix[3], pMatrix[4], pMatrix[5]);
m_arrMatrix[0] = pMatrix[0]; m_arrMatrix[1] = pMatrix[1];
m_arrMatrix[2] = pMatrix[2]; m_arrMatrix[3] = pMatrix[3];
m_arrMatrix[4] = pMatrix[4]; m_arrMatrix[5] = pMatrix[5];
}
}
void RedactOutputDev::DrawPath(const LONG& lType)
{
m_pRenderer->m_oCommandManager.Flush();
DoStateOp();
bool bStroke = lType & c_nStroke;
bool bFill = lType & c_nWindingFillMode;
bool bEoFill = lType & c_nEvenOddFillMode;
m_pPage->GrSave();
UpdateTransform();
m_pRenderer->m_oPath.Draw(m_pPage, bStroke, bFill, bEoFill);
m_pPage->GrRestore();
}
void RedactOutputDev::UpdateTransform()
{
m_pPage->SetTransform(m_arrMatrix[0], m_arrMatrix[1], m_arrMatrix[2], m_arrMatrix[3], m_arrMatrix[4], m_arrMatrix[5]);
}
void RedactOutputDev::AddClip(GfxState* pGState, GfxRedactState* pState, int nIndex)
{
m_pRenderer->m_oCommandManager.Flush();
DoStateOp();
GfxClip* pClip = pState->m_pClip;
GfxPath* pPath = pClip->GetPath(nIndex);
int nClipFlag = pClip->GetClipFlag(nIndex);;
m_pRenderer->m_lClipMode = nClipFlag;
GfxClipMatrix oMatrix = pClip->m_vMatrix[nIndex];
double pMatrix[6] = { oMatrix.dA, oMatrix.dB, oMatrix.dC, oMatrix.dD, oMatrix.dE, oMatrix.dF };
DoPath(pGState, pPath, pMatrix);
UpdateTransform();
m_pRenderer->m_oPath.Clip(m_pPage, c_nClipRegionTypeEvenOdd & m_pRenderer->m_lClipMode);
m_pRenderer->m_oPath.Clear();
}
void RedactOutputDev::DoStateOp()
{
if (m_sStates.empty() || m_sStates.back().m_arrOp.empty())
return;
CStream* pStream = m_pPage->GetStream();
for (int i = 0; i < m_sStates.back().m_arrOp.size(); ++i)
{
auto arrOp = m_sStates.back().m_arrOp;
pStream->WriteStr(arrOp[i].first.c_str());
pStream->WriteChar(' ');
pStream->WriteStr(arrOp[i].second.c_str());
pStream->WriteStr("\012");
}
m_sStates.back().m_arrOp.clear();
}
}