/* * (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& 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 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 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 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 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& 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& 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 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 arrForStroke; Aggplus::CGraphicsPath oPath, oPathResult; if (bEoFill) oPath.SetRuler(true); if (bStroke) { std::vector> 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 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(); } }