3742 lines
104 KiB
C++
3742 lines
104 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 "PdfEditor.h"
|
||
|
||
#include "../DesktopEditor/common/Path.h"
|
||
#include "../DesktopEditor/graphics/commands/AnnotField.h"
|
||
|
||
#include "SrcReader/Adaptors.h"
|
||
#include "SrcReader/PdfAnnot.h"
|
||
#include "lib/xpdf/PDFDoc.h"
|
||
#include "lib/xpdf/AcroForm.h"
|
||
#include "lib/xpdf/TextString.h"
|
||
#include "lib/xpdf/Lexer.h"
|
||
#include "lib/xpdf/Parser.h"
|
||
#include "lib/xpdf/Outline.h"
|
||
#include "lib/xpdf/Link.h"
|
||
#include "lib/xpdf/Stream.h"
|
||
|
||
#include "SrcWriter/Catalog.h"
|
||
#include "SrcWriter/EncryptDictionary.h"
|
||
#include "SrcWriter/Info.h"
|
||
#include "SrcWriter/ResourcesDictionary.h"
|
||
#include "SrcWriter/Streams.h"
|
||
#include "SrcWriter/Destination.h"
|
||
#include "SrcWriter/Outline.h"
|
||
#include "SrcWriter/GState.h"
|
||
#include "SrcWriter/RedactOutputDev.h"
|
||
|
||
#define AddToObject(oVal)\
|
||
{\
|
||
if (pObj->GetType() == PdfWriter::object_type_DICT)\
|
||
((PdfWriter::CDictObject*)pObj)->Add(sKey, oVal);\
|
||
else if (pObj->GetType() == PdfWriter::object_type_ARRAY)\
|
||
((PdfWriter::CArrayObject*)pObj)->Add(oVal);\
|
||
}
|
||
|
||
void DictToCDictObject(Object* obj, PdfWriter::CObjectBase* pObj, const std::string& sKey, bool bMakeBinary = false)
|
||
{
|
||
Object oTemp;
|
||
switch (obj->getType())
|
||
{
|
||
case objBool:
|
||
{
|
||
bool b = obj->getBool() == gTrue;
|
||
AddToObject(b)
|
||
break;
|
||
}
|
||
case objInt:
|
||
{
|
||
AddToObject(obj->getInt())
|
||
break;
|
||
}
|
||
case objReal:
|
||
{
|
||
AddToObject(obj->getReal())
|
||
break;
|
||
}
|
||
case objString:
|
||
{
|
||
GString* str = obj->getString();
|
||
if (str->isBinary() || bMakeBinary)
|
||
{
|
||
int nLength = str->getLength();
|
||
BYTE* arrId = new BYTE[nLength];
|
||
for (int nIndex = 0; nIndex < nLength; ++nIndex)
|
||
arrId[nIndex] = str->getChar(nIndex);
|
||
AddToObject(new PdfWriter::CBinaryObject(arrId, nLength, false));
|
||
}
|
||
else
|
||
{
|
||
TextString* s = new TextString(str);
|
||
std::string sValue = NSStringExt::CConverter::GetUtf8FromUTF32(s->getUnicode(), s->getLength());
|
||
AddToObject(new PdfWriter::CStringObject(sValue.c_str(), !s->isPDFDocEncoding()))
|
||
delete s;
|
||
}
|
||
break;
|
||
}
|
||
case objName:
|
||
{
|
||
AddToObject(obj->getName())
|
||
break;
|
||
}
|
||
case objNull:
|
||
{
|
||
AddToObject(new PdfWriter::CNullObject())
|
||
break;
|
||
}
|
||
case objArray:
|
||
{
|
||
PdfWriter::CArrayObject* pArray = new PdfWriter::CArrayObject();
|
||
AddToObject(pArray)
|
||
for (int nIndex = 0; nIndex < obj->arrayGetLength(); ++nIndex)
|
||
{
|
||
obj->arrayGetNF(nIndex, &oTemp);
|
||
DictToCDictObject(&oTemp, pArray, "");
|
||
oTemp.free();
|
||
}
|
||
break;
|
||
}
|
||
case objDict:
|
||
{
|
||
PdfWriter::CDictObject* pDict = new PdfWriter::CDictObject();
|
||
AddToObject(pDict);
|
||
for (int nIndex = 0; nIndex < obj->dictGetLength(); ++nIndex)
|
||
{
|
||
char* chKey = obj->dictGetKey(nIndex);
|
||
obj->dictGetValNF(nIndex, &oTemp);
|
||
DictToCDictObject(&oTemp, pDict, chKey);
|
||
oTemp.free();
|
||
}
|
||
break;
|
||
}
|
||
case objRef:
|
||
{
|
||
PdfWriter::CObjectBase* pBase = new PdfWriter::CObjectBase();
|
||
pBase->SetRef(obj->getRefNum(), obj->getRefGen());
|
||
AddToObject(new PdfWriter::CProxyObject(pBase, true))
|
||
break;
|
||
}
|
||
case objNone:
|
||
{
|
||
AddToObject("None")
|
||
break;
|
||
}
|
||
case objStream:
|
||
case objCmd:
|
||
case objError:
|
||
case objEOF:
|
||
break;
|
||
}
|
||
}
|
||
bool SplitSkipDict(Object* obj, CObjectsManager* pManager, int nStartRefID)
|
||
{
|
||
Object oTemp, oTemp2;
|
||
if (obj->dictLookup("Type", &oTemp)->isName("Page"))
|
||
{
|
||
oTemp.free();
|
||
return true;
|
||
}
|
||
else if (oTemp.isName("Annot") && obj->dictLookupNF("P", &oTemp2)->isRef())
|
||
{
|
||
PdfWriter::CObjectBase* pObj = pManager->GetObj(oTemp2.getRefNum() + nStartRefID);
|
||
if (!pObj)
|
||
{
|
||
oTemp.free(); oTemp2.free();
|
||
return true;
|
||
}
|
||
}
|
||
oTemp.free(); oTemp2.free();
|
||
|
||
if (obj->dictLookup("S", &oTemp)->isName("GoTo"))
|
||
{
|
||
oTemp.free();
|
||
if (obj->dictLookup("D", &oTemp)->isArray() && oTemp.arrayGetLength() > 1 && oTemp.arrayGetNF(0, &oTemp2)->isRef())
|
||
{
|
||
PdfWriter::CObjectBase* pObj = pManager->GetObj(oTemp2.getRefNum() + nStartRefID);
|
||
if (!pObj)
|
||
{
|
||
oTemp.free(); oTemp2.free();
|
||
return true;
|
||
}
|
||
}
|
||
oTemp2.free();
|
||
}
|
||
oTemp.free();
|
||
|
||
return false;
|
||
}
|
||
PdfWriter::CAnnotation* CreateAnnot(Object* oAnnot, Object* oType, PdfWriter::CXref* pXref)
|
||
{
|
||
PdfWriter::CAnnotation* pAnnot = NULL;
|
||
if (oType->isName("Text"))
|
||
pAnnot = new PdfWriter::CTextAnnotation(pXref);
|
||
else if (oType->isName("Ink"))
|
||
pAnnot = new PdfWriter::CInkAnnotation(pXref);
|
||
else if (oType->isName("Line"))
|
||
pAnnot = new PdfWriter::CLineAnnotation(pXref);
|
||
else if (oType->isName("Highlight") || oType->isName("Underline") || oType->isName("Squiggly") || oType->isName("StrikeOut"))
|
||
pAnnot = new PdfWriter::CTextMarkupAnnotation(pXref);
|
||
else if (oType->isName("Square") || oType->isName("Circle"))
|
||
pAnnot = new PdfWriter::CSquareCircleAnnotation(pXref);
|
||
else if (oType->isName("Polygon") || oType->isName("PolyLine"))
|
||
pAnnot = new PdfWriter::CPolygonLineAnnotation(pXref);
|
||
else if (oType->isName("FreeText"))
|
||
pAnnot = new PdfWriter::CFreeTextAnnotation(pXref);
|
||
else if (oType->isName("Caret"))
|
||
pAnnot = new PdfWriter::CCaretAnnotation(pXref);
|
||
else if (oType->isName("Stamp"))
|
||
pAnnot = new PdfWriter::CStampAnnotation(pXref);
|
||
else if (oType->isName("Redact"))
|
||
pAnnot = new PdfWriter::CRedactAnnotation(pXref);
|
||
else if (oType->isName("Popup"))
|
||
pAnnot = new PdfWriter::CPopupAnnotation(pXref);
|
||
else if (oType->isName("Widget"))
|
||
{
|
||
char* sName = NULL;
|
||
Object oFT;
|
||
if (oAnnot->dictLookup("FT", &oFT)->isName())
|
||
sName = oFT.getName();
|
||
|
||
if (!sName)
|
||
{
|
||
Object oParent, oParent2;
|
||
oAnnot->dictLookup("Parent", &oParent);
|
||
while (oParent.isDict())
|
||
{
|
||
if (oParent.dictLookup("FT", &oFT)->isName())
|
||
{
|
||
sName = oFT.getName();
|
||
break;
|
||
}
|
||
oFT.free();
|
||
oParent.dictLookup("Parent", &oParent2);
|
||
oParent.free();
|
||
oParent = oParent2;
|
||
}
|
||
oParent.free();
|
||
}
|
||
|
||
if (!sName)
|
||
{
|
||
oFT.free();
|
||
return new PdfWriter::CWidgetAnnotation(pXref, PdfWriter::EAnnotType::AnnotWidget);
|
||
}
|
||
if (strcmp("Btn", sName) == 0)
|
||
{
|
||
bool bPushButton = false;
|
||
oFT.free();
|
||
int nFf = 0;
|
||
if (oAnnot->dictLookup("Ff", &oFT)->isInt())
|
||
nFf = oFT.getInt();
|
||
if (!nFf)
|
||
{
|
||
Object oParent, oParent2;
|
||
oAnnot->dictLookup("Parent", &oParent);
|
||
while (oParent.isDict())
|
||
{
|
||
if (oParent.dictLookup("Ff", &oFT)->isInt())
|
||
{
|
||
nFf = oFT.getInt();
|
||
break;
|
||
}
|
||
oFT.free();
|
||
oParent.dictLookup("Parent", &oParent2);
|
||
oParent.free();
|
||
oParent = oParent2;
|
||
}
|
||
oParent.free();
|
||
}
|
||
|
||
bPushButton = (bool)((nFf >> 16) & 1);
|
||
if (bPushButton)
|
||
pAnnot = new PdfWriter::CPushButtonWidget(pXref);
|
||
else
|
||
pAnnot = new PdfWriter::CCheckBoxWidget(pXref);
|
||
}
|
||
else if (strcmp("Tx", sName) == 0)
|
||
pAnnot = new PdfWriter::CTextWidget(pXref);
|
||
else if (strcmp("Ch", sName) == 0)
|
||
pAnnot = new PdfWriter::CChoiceWidget(pXref);
|
||
else if (strcmp("Sig", sName) == 0)
|
||
pAnnot = new PdfWriter::CSignatureWidget(pXref);
|
||
else
|
||
pAnnot = new PdfWriter::CWidgetAnnotation(pXref, PdfWriter::EAnnotType::AnnotWidget);
|
||
oFT.free();
|
||
}
|
||
return pAnnot;
|
||
}
|
||
PdfWriter::CExtGrState* CreateExtGState(Object* oState)
|
||
{
|
||
PdfWriter::CExtGrState* pState = new PdfWriter::CExtGrState(NULL);
|
||
return pState;
|
||
}
|
||
PdfWriter::CObjectBase* DictToCDictObject2(Object* obj, PdfWriter::CDocument* pDoc, XRef* xref, CObjectsManager* pManager, int nStartRefID, int nAddObjToXRef = 0, bool bUndecodedStream = true)
|
||
{
|
||
PdfWriter::CObjectBase* pBase = NULL;
|
||
Object oTemp;
|
||
switch (obj->getType())
|
||
{
|
||
case objBool:
|
||
{
|
||
pBase = new PdfWriter::CBoolObject(obj->getBool() == gTrue);
|
||
break;
|
||
}
|
||
case objInt:
|
||
{
|
||
pBase = new PdfWriter::CNumberObject(obj->getInt());
|
||
break;
|
||
}
|
||
case objReal:
|
||
{
|
||
pBase = new PdfWriter::CRealObject(obj->getReal());
|
||
break;
|
||
}
|
||
case objString:
|
||
{
|
||
GString* str = obj->getString();
|
||
if (str->isBinary())
|
||
{
|
||
int nLength = str->getLength();
|
||
BYTE* arrId = new BYTE[nLength];
|
||
for (int nIndex = 0; nIndex < nLength; ++nIndex)
|
||
arrId[nIndex] = str->getChar(nIndex);
|
||
pBase = new PdfWriter::CBinaryObject(arrId, nLength, false);
|
||
}
|
||
else
|
||
{
|
||
TextString* s = new TextString(str);
|
||
std::string sValue = NSStringExt::CConverter::GetUtf8FromUTF32(s->getUnicode(), s->getLength());
|
||
pBase = new PdfWriter::CStringObject(sValue.c_str(), !s->isPDFDocEncoding());
|
||
delete s;
|
||
}
|
||
break;
|
||
}
|
||
case objName:
|
||
{
|
||
pBase = new PdfWriter::CNameObject(obj->getName());
|
||
break;
|
||
}
|
||
case objNull:
|
||
{
|
||
pBase = new PdfWriter::CNullObject();
|
||
break;
|
||
}
|
||
case objArray:
|
||
{
|
||
PdfWriter::CArrayObject* pArray = new PdfWriter::CArrayObject();
|
||
if (nAddObjToXRef > 0)
|
||
{
|
||
pDoc->AddObject(pArray);
|
||
pManager->AddObj(nAddObjToXRef + nStartRefID, pArray);
|
||
nAddObjToXRef = 0;
|
||
}
|
||
for (int nIndex = 0; nIndex < obj->arrayGetLength(); ++nIndex)
|
||
{
|
||
obj->arrayGetNF(nIndex, &oTemp);
|
||
pBase = DictToCDictObject2(&oTemp, pDoc, xref, pManager, nStartRefID, 0, bUndecodedStream);
|
||
pArray->Add(pBase);
|
||
oTemp.free();
|
||
}
|
||
pBase = pArray;
|
||
break;
|
||
}
|
||
case objDict:
|
||
{
|
||
if (SplitSkipDict(obj, pManager, nStartRefID))
|
||
return NULL;
|
||
|
||
Object oType, oSubtype;
|
||
PdfWriter::CDictObject* pDict = NULL;
|
||
if (obj->dictLookup("Subtype", &oSubtype)->isName())
|
||
{
|
||
PdfWriter::CAnnotation* pAnnot = CreateAnnot(obj, &oSubtype, NULL);
|
||
if (pAnnot)
|
||
{
|
||
pDoc->AddAnnotation(nAddObjToXRef + nStartRefID, pAnnot);
|
||
pDict = pAnnot;
|
||
}
|
||
}
|
||
oSubtype.free();
|
||
|
||
if (obj->dictLookup("Type", &oType)->isName("ExtGState"))
|
||
{
|
||
PdfWriter::CExtGrState* pState = CreateExtGState(obj);
|
||
if (pState)
|
||
{
|
||
pDoc->AddExtGState(pState);
|
||
pDict = pState;
|
||
}
|
||
}
|
||
oType.free();
|
||
|
||
if (!pDict)
|
||
pDict = new PdfWriter::CDictObject();
|
||
if (nAddObjToXRef > 0)
|
||
{
|
||
pDoc->AddObject(pDict);
|
||
pManager->AddObj(nAddObjToXRef + nStartRefID, pDict);
|
||
nAddObjToXRef = 0;
|
||
}
|
||
for (int nIndex = 0; nIndex < obj->dictGetLength(); ++nIndex)
|
||
{
|
||
char* chKey = obj->dictGetKey(nIndex);
|
||
obj->dictGetValNF(nIndex, &oTemp);
|
||
pBase = DictToCDictObject2(&oTemp, pDoc, xref, pManager, nStartRefID, 0, bUndecodedStream);
|
||
pDict->Add(chKey, pBase);
|
||
oTemp.free();
|
||
}
|
||
pBase = pDict;
|
||
break;
|
||
}
|
||
case objRef:
|
||
{
|
||
int nObjNum = obj->getRefNum();
|
||
PdfWriter::CObjectBase* pObj = pManager->GetObj(nObjNum + nStartRefID);
|
||
if (pObj)
|
||
{
|
||
pManager->IncRefCount(nObjNum + nStartRefID);
|
||
return pObj;
|
||
}
|
||
|
||
obj->fetch(xref, &oTemp);
|
||
pBase = DictToCDictObject2(&oTemp, pDoc, xref, pManager, nStartRefID, nObjNum, bUndecodedStream);
|
||
oTemp.free();
|
||
break;
|
||
}
|
||
case objNone:
|
||
{
|
||
pBase = new PdfWriter::CNameObject("None");
|
||
break;
|
||
}
|
||
case objStream:
|
||
{
|
||
PdfWriter::CDictObject* pDict = new PdfWriter::CDictObject();
|
||
if (nAddObjToXRef > 0)
|
||
{
|
||
pDoc->AddObject(pDict);
|
||
pManager->AddObj(nAddObjToXRef + nStartRefID, pDict);
|
||
nAddObjToXRef = 0;
|
||
}
|
||
|
||
Dict* pODict = obj->streamGetDict();
|
||
int nLength = 0;
|
||
for (int nIndex = 0; nIndex < pODict->getLength(); ++nIndex)
|
||
{
|
||
char* chKey = pODict->getKey(nIndex);
|
||
if (strcmp("Length", chKey) == 0)
|
||
{
|
||
pODict->getVal(nIndex, &oTemp);
|
||
nLength = oTemp.getNum();
|
||
|
||
pBase = new PdfWriter::CNumberObject(nLength);
|
||
pDoc->AddObject(pBase);
|
||
pDict->Add(chKey, pBase);
|
||
oTemp.free();
|
||
continue;
|
||
}
|
||
pODict->getValNF(nIndex, &oTemp);
|
||
pBase = DictToCDictObject2(&oTemp, pDoc, xref, pManager, nStartRefID);
|
||
pDict->Add(chKey, pBase);
|
||
oTemp.free();
|
||
}
|
||
pBase = pDict;
|
||
|
||
PdfWriter::CStream* pStream = new PdfWriter::CMemoryStream(nLength);
|
||
pDict->SetStream(pStream);
|
||
Stream* pOStream = bUndecodedStream ? obj->getStream()->getUndecodedStream() : obj->getStream();
|
||
pOStream->reset();
|
||
int nChar = pOStream->getChar();
|
||
while (nChar != EOF)
|
||
{
|
||
pStream->WriteChar(nChar);
|
||
nChar = pOStream->getChar();
|
||
}
|
||
break;
|
||
}
|
||
case objCmd:
|
||
case objError:
|
||
case objEOF:
|
||
break;
|
||
}
|
||
if (nAddObjToXRef > 0)
|
||
{
|
||
pDoc->AddObject(pBase);
|
||
pManager->AddObj(nAddObjToXRef + nStartRefID, pBase);
|
||
}
|
||
|
||
return pBase;
|
||
}
|
||
void AddWidgetParent(PdfWriter::CDocument* pDoc, CObjectsManager* pManager, PdfWriter::CObjectBase* pObj)
|
||
{
|
||
if (pObj->GetType() != PdfWriter::object_type_DICT)
|
||
return;
|
||
PdfWriter::CDictObject* pDict = dynamic_cast<PdfWriter::CDictObject*>(pObj);
|
||
if (!pDict)
|
||
return;
|
||
|
||
int nID = pManager->FindObj(pObj);
|
||
if (nID < 0)
|
||
return;
|
||
|
||
if (pDict->GetDictType() == PdfWriter::dict_type_UNKNOWN)
|
||
{
|
||
if (pDoc->GetParent(nID))
|
||
return;
|
||
pDoc->AddParent(nID, pDict);
|
||
}
|
||
|
||
PdfWriter::CObjectBase* pObjParent = pDict->Get("Parent");
|
||
if (pObjParent && pObjParent->GetType() == PdfWriter::object_type_DICT)
|
||
AddWidgetParent(pDoc, pManager, pObjParent);
|
||
|
||
if (pDict->GetDictType() != PdfWriter::dict_type_UNKNOWN)
|
||
return;
|
||
|
||
PdfWriter::CObjectBase* pObjKids = pDict->Get("Kids");
|
||
if (!pObjKids || pObjKids->GetType() != PdfWriter::object_type_ARRAY)
|
||
return;
|
||
|
||
PdfWriter::CArrayObject* pKids = (PdfWriter::CArrayObject*)pObjKids;
|
||
for (int i = 0; i < pKids->GetCount(); ++i)
|
||
AddWidgetParent(pDoc, pManager, pKids->Get(i));
|
||
}
|
||
PdfWriter::CDictObject* GetWidgetParent(PDFDoc* pdfDoc, PdfWriter::CDocument* pDoc, Object* pParentRef, int nStartRefID)
|
||
{
|
||
if (!pParentRef || !pParentRef->isRef() || !pdfDoc)
|
||
return NULL;
|
||
PdfWriter::CDictObject* pParent = pDoc->GetParent(pParentRef->getRefNum() + nStartRefID);
|
||
if (pParent)
|
||
return pParent;
|
||
|
||
Object oParent;
|
||
if (!pParentRef->fetch(pdfDoc->getXRef(), &oParent)->isDict())
|
||
{
|
||
oParent.free();
|
||
return pParent;
|
||
}
|
||
|
||
PdfWriter::CXref* pXref = NULL;
|
||
pParent = new PdfWriter::CDictObject();
|
||
if (nStartRefID == 0)
|
||
{
|
||
pXref = new PdfWriter::CXref(pDoc, pParentRef->getRefNum());
|
||
pXref->Add(pParent, pParentRef->getRefGen());
|
||
}
|
||
if (!pDoc->EditParent(pXref, pParent, pParentRef->getRefNum() + nStartRefID))
|
||
{
|
||
RELEASEOBJECT(pXref);
|
||
oParent.free();
|
||
return NULL;
|
||
}
|
||
|
||
for (int i = 0; i < oParent.dictGetLength(); ++i)
|
||
{
|
||
char* chKey = oParent.dictGetKey(i);
|
||
if (strcmp("Parent", chKey) == 0)
|
||
{
|
||
Object oParentRef;
|
||
oParent.dictGetValNF(i, &oParentRef);
|
||
PdfWriter::CDictObject* pParent2 = GetWidgetParent(pdfDoc, pDoc, &oParentRef, nStartRefID);
|
||
if (pParent2)
|
||
{
|
||
pParent->Add("Parent", pParent2);
|
||
oParentRef.free();
|
||
continue;
|
||
}
|
||
oParentRef.free();
|
||
}
|
||
Object oTemp;
|
||
oParent.dictGetValNF(i, &oTemp);
|
||
DictToCDictObject(&oTemp, pParent, chKey);
|
||
oTemp.free();
|
||
}
|
||
|
||
oParent.free();
|
||
return pParent;
|
||
}
|
||
HRESULT _ChangePassword(const std::wstring& wsPath, const std::wstring& wsPassword, CPdfReader* _pReader, CPdfWriter* _pWriter)
|
||
{
|
||
if (!_pReader || !_pWriter)
|
||
return S_FALSE;
|
||
PDFDoc* pPDFDocument = _pReader->GetPDFDocument(0);
|
||
if (!pPDFDocument)
|
||
return S_FALSE;
|
||
XRef* xref = pPDFDocument->getXRef();
|
||
if (!xref)
|
||
return S_FALSE;
|
||
Object* trailerDict = xref->getTrailerDict();
|
||
if (!trailerDict)
|
||
return S_FALSE;
|
||
|
||
PdfWriter::CDocument* pDoc = _pWriter->GetDocument();
|
||
PdfWriter::CXref* pXref = new PdfWriter::CXref(pDoc, 0);
|
||
PdfWriter::CXref* m_pXref = new PdfWriter::CXref(pDoc, xref->getNumObjects()); // Для новых объектов
|
||
if (!xref || !pDoc || !pXref || !m_pXref)
|
||
{
|
||
RELEASEOBJECT(pXref);
|
||
RELEASEOBJECT(m_pXref);
|
||
return S_FALSE;
|
||
}
|
||
pXref->SetPrev(m_pXref);
|
||
|
||
for (int i = 0; i < xref->getSize(); ++i)
|
||
{
|
||
XRefEntry* pEntry = xref->getEntry(i);
|
||
if (pEntry->type == xrefEntryFree)
|
||
continue;
|
||
|
||
if (i != pXref->GetSizeXRef())
|
||
{
|
||
PdfWriter::CXref* pXref2 = new PdfWriter::CXref(pDoc, i);
|
||
pXref2->SetPrev(pXref);
|
||
pXref = pXref2;
|
||
}
|
||
|
||
Object oTemp;
|
||
xref->fetch(i, pEntry->type == xrefEntryCompressed ? 0 : pEntry->gen, &oTemp);
|
||
PdfWriter::CObjectBase* pObj = NULL;
|
||
|
||
switch (oTemp.getType())
|
||
{
|
||
case objBool:
|
||
{
|
||
pObj = new PdfWriter::CBoolObject(oTemp.getBool());
|
||
break;
|
||
}
|
||
case objInt:
|
||
{
|
||
pObj = new PdfWriter::CNumberObject(oTemp.getInt());
|
||
break;
|
||
}
|
||
case objReal:
|
||
{
|
||
pObj = new PdfWriter::CRealObject(oTemp.getReal());
|
||
break;
|
||
}
|
||
case objString:
|
||
{
|
||
GString* str = oTemp.getString();
|
||
if (str->isBinary())
|
||
{
|
||
int nLength = str->getLength();
|
||
BYTE* arrId = new BYTE[nLength];
|
||
for (int nIndex = 0; nIndex < nLength; ++nIndex)
|
||
arrId[nIndex] = str->getChar(nIndex);
|
||
pObj = new PdfWriter::CBinaryObject(arrId, nLength, false);
|
||
}
|
||
else
|
||
{
|
||
TextString* s = new TextString(str);
|
||
std::string sValue = NSStringExt::CConverter::GetUtf8FromUTF32(s->getUnicode(), s->getLength());
|
||
pObj = new PdfWriter::CStringObject(sValue.c_str(), !s->isPDFDocEncoding());
|
||
delete s;
|
||
}
|
||
break;
|
||
}
|
||
case objName:
|
||
{
|
||
pObj = new PdfWriter::CNameObject(oTemp.getName());
|
||
break;
|
||
}
|
||
case objNull:
|
||
{
|
||
pObj = new PdfWriter::CNullObject();
|
||
break;
|
||
}
|
||
case objArray:
|
||
{
|
||
pObj = new PdfWriter::CArrayObject();
|
||
|
||
for (int nIndex = 0; nIndex < oTemp.arrayGetLength(); ++nIndex)
|
||
{
|
||
Object oT;
|
||
oTemp.arrayGetNF(nIndex, &oT);
|
||
DictToCDictObject(&oT, pObj, "");
|
||
oT.free();
|
||
}
|
||
break;
|
||
}
|
||
case objDict:
|
||
{
|
||
pObj = new PdfWriter::CDictObject();
|
||
|
||
for (int nIndex = 0; nIndex < oTemp.dictGetLength(); ++nIndex)
|
||
{
|
||
Object oT;
|
||
char* chKey = oTemp.dictGetKey(nIndex);
|
||
oTemp.dictGetValNF(nIndex, &oT);
|
||
DictToCDictObject(&oT, pObj, chKey);
|
||
oT.free();
|
||
}
|
||
break;
|
||
}
|
||
case objRef:
|
||
{
|
||
PdfWriter::CObjectBase* pBase = new PdfWriter::CObjectBase();
|
||
pBase->SetRef(oTemp.getRefNum(), oTemp.getRefGen());
|
||
pObj = new PdfWriter::CProxyObject(pBase, true);
|
||
break;
|
||
}
|
||
case objStream:
|
||
{
|
||
Dict* pDict = oTemp.streamGetDict();
|
||
Object oObjStm;
|
||
if (pDict->lookup("Type", &oObjStm)->isName("ObjStm"))
|
||
{
|
||
oObjStm.free();
|
||
break;
|
||
}
|
||
oObjStm.free();
|
||
|
||
PdfWriter::CDictObject* pDObj = new PdfWriter::CDictObject();
|
||
pObj = pDObj;
|
||
|
||
int nLength = 0;
|
||
for (int nIndex = 0; nIndex < pDict->getLength(); ++nIndex)
|
||
{
|
||
Object oT;
|
||
char* chKey = pDict->getKey(nIndex);
|
||
if (strcmp("Length", chKey) == 0)
|
||
{
|
||
Object oLength;
|
||
nLength = pDict->getVal(nIndex, &oLength)->isInt() ? oLength.getInt() : 0;
|
||
oLength.free();
|
||
continue;
|
||
}
|
||
pDict->getValNF(nIndex, &oT);
|
||
DictToCDictObject(&oT, pObj, chKey);
|
||
oT.free();
|
||
}
|
||
|
||
PdfWriter::CStream* pStream = new PdfWriter::CMemoryStream();
|
||
pDObj->SetStream(m_pXref, pStream, false);
|
||
|
||
Stream* pImage = oTemp.getStream()->getUndecodedStream();
|
||
pImage->reset();
|
||
for (int nI = 0; nI < nLength; ++nI)
|
||
pStream->WriteChar(pImage->getChar());
|
||
break;
|
||
}
|
||
case objNone:
|
||
case objCmd:
|
||
case objError:
|
||
case objEOF:
|
||
default:
|
||
break;
|
||
}
|
||
oTemp.free();
|
||
|
||
if (pObj)
|
||
pXref->Add(pObj);
|
||
}
|
||
|
||
PdfWriter::CDictObject* pTrailer = pXref->GetTrailer();
|
||
for (int nIndex = 0; nIndex < trailerDict->dictGetLength(); ++nIndex)
|
||
{
|
||
Object oTemp;
|
||
char* chKey = trailerDict->dictGetKey(nIndex);
|
||
if (strcmp("Root", chKey) == 0 || strcmp("Info", chKey) == 0)
|
||
{
|
||
trailerDict->dictGetValNF(nIndex, &oTemp);
|
||
DictToCDictObject(&oTemp, pTrailer, chKey);
|
||
}
|
||
oTemp.free();
|
||
}
|
||
|
||
bool bRes = pDoc->SaveNewWithPassword(pXref, m_pXref, wsPath, wsPassword, wsPassword, pTrailer);
|
||
|
||
RELEASEOBJECT(pXref);
|
||
|
||
return bRes ? S_OK : S_FALSE;
|
||
}
|
||
void GetCTM(XRef* pXref, Object* oPage, double* dCTM)
|
||
{
|
||
if (!oPage || !oPage->isDict())
|
||
return;
|
||
|
||
Object oContents;
|
||
if (!oPage->dictLookup("Contents", &oContents) || (!oContents.isArray() && !oContents.isStream()))
|
||
{
|
||
oContents.free();
|
||
return;
|
||
}
|
||
|
||
Parser* parser = new Parser(pXref, new Lexer(pXref, &oContents), gFalse);
|
||
|
||
int nNumArgs = 0;
|
||
Object oObj;
|
||
Object pArgs[maxArgs];
|
||
|
||
parser->getObj(&oObj);
|
||
while (!oObj.isEOF())
|
||
{
|
||
if (oObj.isCmd())
|
||
{
|
||
if (oObj.isCmd("q"))
|
||
{
|
||
Object obj;
|
||
parser->getObj(&obj);
|
||
while (!obj.isEOF() && !obj.isCmd("Q"))
|
||
{
|
||
obj.free();
|
||
parser->getObj(&obj);
|
||
}
|
||
obj.free();
|
||
}
|
||
else if (oObj.isCmd("cm") && nNumArgs > 5)
|
||
{
|
||
double a1 = dCTM[0];
|
||
double b1 = dCTM[1];
|
||
double c1 = dCTM[2];
|
||
double d1 = dCTM[3];
|
||
|
||
dCTM[0] = pArgs[0].getNum() * a1 + pArgs[1].getNum() * c1;
|
||
dCTM[1] = pArgs[0].getNum() * b1 + pArgs[1].getNum() * d1;
|
||
dCTM[2] = pArgs[2].getNum() * a1 + pArgs[3].getNum() * c1;
|
||
dCTM[3] = pArgs[2].getNum() * b1 + pArgs[3].getNum() * d1;
|
||
dCTM[4] = pArgs[4].getNum() * a1 + pArgs[5].getNum() * c1 + dCTM[4];
|
||
dCTM[5] = pArgs[4].getNum() * b1 + pArgs[5].getNum() * d1 + dCTM[5];
|
||
}
|
||
oObj.free();
|
||
for (int i = 0; i < nNumArgs; ++i)
|
||
pArgs[i].free();
|
||
nNumArgs = 0;
|
||
}
|
||
else if (nNumArgs < maxArgs)
|
||
pArgs[nNumArgs++] = oObj;
|
||
|
||
parser->getObj(&oObj);
|
||
}
|
||
oObj.free();
|
||
for (int i = 0; i < nNumArgs; ++i)
|
||
pArgs[i].free();
|
||
RELEASEOBJECT(parser);
|
||
|
||
oContents.free();
|
||
}
|
||
|
||
void CObjectsManager::AddObj(int nID, PdfWriter::CObjectBase* pObj)
|
||
{
|
||
if (m_mUniqueRef.find(nID) == m_mUniqueRef.end())
|
||
m_mUniqueRef[nID] = { pObj, 1 };
|
||
}
|
||
void CObjectsManager::RemoveObj(int nID)
|
||
{
|
||
m_mUniqueRef.erase(nID);
|
||
}
|
||
PdfWriter::CObjectBase* CObjectsManager::GetObj(int nID)
|
||
{
|
||
if (m_mUniqueRef.find(nID) != m_mUniqueRef.end())
|
||
return m_mUniqueRef[nID].pObj;
|
||
return NULL;
|
||
}
|
||
bool CObjectsManager::IncRefCount(int nID)
|
||
{
|
||
if (m_mUniqueRef.find(nID) != m_mUniqueRef.end())
|
||
{
|
||
m_mUniqueRef[nID].nRefCount++;
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
bool CObjectsManager::DecRefCount(int nID)
|
||
{
|
||
if (m_mUniqueRef.find(nID) != m_mUniqueRef.end())
|
||
{
|
||
if (m_pDoc)
|
||
m_pDoc->RemoveObj(m_mUniqueRef[nID].pObj);
|
||
else
|
||
m_mUniqueRef[nID].pObj->SetHidden();
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
int CObjectsManager::FindObj(PdfWriter::CObjectBase* pObj)
|
||
{
|
||
std::map<int, CObjectInfo>::iterator it = std::find_if(m_mUniqueRef.begin(), m_mUniqueRef.end(), [pObj](const std::pair<int, CObjectInfo>& t){ return t.second.pObj == pObj; });
|
||
if (it != m_mUniqueRef.end())
|
||
return it->first;
|
||
return -1;
|
||
}
|
||
void CObjectsManager::DeleteObjTree(Object* obj, XRef* xref, int nStartRefID)
|
||
{
|
||
Object oTemp;
|
||
switch (obj->getType())
|
||
{
|
||
case objBool:
|
||
case objInt:
|
||
case objReal:
|
||
case objString:
|
||
case objName:
|
||
case objNull:
|
||
case objNone:
|
||
case objCmd:
|
||
case objError:
|
||
case objEOF:
|
||
break;
|
||
case objArray:
|
||
{
|
||
for (int nIndex = 0; nIndex < obj->arrayGetLength(); ++nIndex)
|
||
{
|
||
obj->arrayGetNF(nIndex, &oTemp);
|
||
DeleteObjTree(&oTemp, xref, nStartRefID);
|
||
oTemp.free();
|
||
}
|
||
break;
|
||
}
|
||
case objDict:
|
||
{
|
||
if (SplitSkipDict(obj, this, nStartRefID))
|
||
return;
|
||
for (int nIndex = 0; nIndex < obj->dictGetLength(); ++nIndex)
|
||
{
|
||
obj->dictGetValNF(nIndex, &oTemp);
|
||
DeleteObjTree(&oTemp, xref, nStartRefID);
|
||
oTemp.free();
|
||
}
|
||
break;
|
||
}
|
||
case objRef:
|
||
{
|
||
int nObjNum = obj->getRefNum();
|
||
PdfWriter::CObjectBase* pObj = GetObj(nObjNum + nStartRefID);
|
||
if (pObj && DecRefCount(nObjNum + nStartRefID))
|
||
return;
|
||
|
||
obj->fetch(xref, &oTemp);
|
||
DeleteObjTree(&oTemp, xref, nStartRefID);
|
||
oTemp.free();
|
||
break;
|
||
}
|
||
case objStream:
|
||
{
|
||
Dict* pODict = obj->streamGetDict();
|
||
for (int nIndex = 0; nIndex < pODict->getLength(); ++nIndex)
|
||
{
|
||
pODict->getValNF(nIndex, &oTemp);
|
||
DeleteObjTree(&oTemp, xref, nStartRefID);
|
||
oTemp.free();
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
void CObjectsManager::SetDoc(PdfWriter::CDocument* pDoc) { m_pDoc = pDoc; }
|
||
|
||
CPdfEditor::CPdfEditor(const std::wstring& _wsSrcFile, const std::wstring& _wsPassword, const std::wstring& _wsDstFile, CPdfReader* _pReader, CPdfWriter* _pWriter, Mode nMode)
|
||
{
|
||
m_wsSrcFile = _wsSrcFile;
|
||
m_wsDstFile = _wsDstFile;
|
||
m_wsPassword = _wsPassword;
|
||
m_pReader = _pReader;
|
||
m_pWriter = _pWriter;
|
||
m_nEditPage = -1;
|
||
m_nError = 0;
|
||
m_nMode = Mode::Unknown;
|
||
|
||
PDFDoc* pPDFDocument = m_pReader->GetPDFDocument(0);
|
||
if (!pPDFDocument)
|
||
{
|
||
m_nError = 1;
|
||
return;
|
||
}
|
||
|
||
XRef* xref = pPDFDocument->getXRef();
|
||
PdfWriter::CDocument* pDoc = m_pWriter->GetDocument();
|
||
if (!xref || !pDoc)
|
||
{
|
||
m_nError = 1;
|
||
return;
|
||
}
|
||
|
||
m_nOriginIndex = m_pReader->GetNumPages();
|
||
SetMode(nMode);
|
||
}
|
||
void CPdfEditor::SetMode(Mode nMode)
|
||
{
|
||
if (m_nMode != Mode::Unknown)
|
||
return;
|
||
m_nMode = nMode;
|
||
if (m_nMode == Mode::WriteAppend)
|
||
IncrementalUpdates();
|
||
else if (m_nMode == Mode::WriteNew)
|
||
{
|
||
PdfWriter::CDocument* pDoc = m_pWriter->GetDocument();
|
||
PdfWriter::CPageTree* pPageTree = pDoc->GetPageTree();
|
||
m_mObjManager.SetDoc(pDoc);
|
||
int nPages = m_pReader->GetNumPages();
|
||
pPageTree->CreateFakePages(nPages);
|
||
}
|
||
}
|
||
bool CPdfEditor::IncrementalUpdates()
|
||
{
|
||
if (m_nMode != Mode::Unknown)
|
||
return true;
|
||
|
||
m_nMode = Mode::WriteAppend;
|
||
PDFDoc* pPDFDocument = m_pReader->GetPDFDocument(0);
|
||
XRef* xref = pPDFDocument->getXRef();
|
||
PdfWriter::CDocument* pDoc = m_pWriter->GetDocument();
|
||
|
||
std::string sPathUtf8New = U_TO_UTF8(m_wsDstFile);
|
||
std::string sPathUtf8Old = U_TO_UTF8(m_wsSrcFile);
|
||
if (sPathUtf8Old == sPathUtf8New || NSSystemPath::NormalizePath(sPathUtf8Old) == NSSystemPath::NormalizePath(sPathUtf8New))
|
||
{
|
||
GString* owner_pswd = NSStrings::CreateString(m_wsPassword);
|
||
GString* user_pswd = NSStrings::CreateString(m_wsPassword);
|
||
GBool bRes = pPDFDocument->makeWritable(true, owner_pswd, user_pswd);
|
||
delete owner_pswd;
|
||
delete user_pswd;
|
||
if (!bRes)
|
||
return false;
|
||
}
|
||
else
|
||
{
|
||
if (!NSFile::CFileBinary::Copy(m_wsSrcFile, m_wsDstFile))
|
||
return false;
|
||
NSFile::CFileBinary oFile;
|
||
if (!oFile.OpenFile(m_wsDstFile, true))
|
||
return false;
|
||
oFile.CloseFile();
|
||
}
|
||
|
||
// Получение каталога и дерева страниц из reader
|
||
Object catDict, catRefObj, pagesRefObj;
|
||
if (!xref->getCatalog(&catDict)->isDict() || !catDict.dictLookupNF("Pages", &pagesRefObj))
|
||
{
|
||
pagesRefObj.free(); catDict.free();
|
||
return false;
|
||
}
|
||
Object* trailer = xref->getTrailerDict();
|
||
if (!trailer || !trailer->isDict() || !trailer->dictLookupNF("Root", &catRefObj)->isRef())
|
||
{
|
||
pagesRefObj.free(); catDict.free(); catRefObj.free();
|
||
return false;
|
||
}
|
||
Ref catRef = catRefObj.getRef();
|
||
catRefObj.free();
|
||
|
||
// Создание каталога для writer
|
||
PdfWriter::CXref* pXref = new PdfWriter::CXref(pDoc, catRef.num);
|
||
if (!pXref)
|
||
{
|
||
pagesRefObj.free(); catDict.free();
|
||
return false;
|
||
}
|
||
PdfWriter::CCatalog* pCatalog = new PdfWriter::CCatalog();
|
||
if (!pCatalog)
|
||
{
|
||
pagesRefObj.free(); catDict.free(); RELEASEOBJECT(pXref);
|
||
return false;
|
||
}
|
||
pXref->Add(pCatalog, catRef.gen);
|
||
PdfWriter::CResourcesDict* pDR = NULL;
|
||
PdfWriter::CXref* pDRXref = NULL;
|
||
for (int nIndex = 0; nIndex < catDict.dictGetLength(); ++nIndex)
|
||
{
|
||
Object oAcroForm;
|
||
char* chKey = catDict.dictGetKey(nIndex);
|
||
if (strcmp("AcroForm", chKey) == 0)
|
||
{
|
||
catDict.dictGetVal(nIndex, &oAcroForm);
|
||
PdfWriter::CDictObject* pAcroForm = new PdfWriter::CDictObject();
|
||
|
||
for (int nIndex = 0; nIndex < oAcroForm.dictGetLength(); ++nIndex)
|
||
{
|
||
Object oTemp2;
|
||
char* chKey = oAcroForm.dictGetKey(nIndex);
|
||
if (strcmp("DR", chKey) == 0)
|
||
{
|
||
if (!oAcroForm.dictGetVal(nIndex, &oTemp2)->isDict())
|
||
{
|
||
oTemp2.free();
|
||
continue;
|
||
}
|
||
|
||
Object oDR;
|
||
oAcroForm.dictGetValNF(nIndex, &oDR);
|
||
int nDRxrefNum = oDR.isRef() ? oDR.getRefNum() : xref->getNumObjects();
|
||
int nDRxrefGen = oDR.isRef() ? oDR.getRefGen() : 0;
|
||
oDR.free();
|
||
pDRXref = new PdfWriter::CXref(pDoc, nDRxrefNum);
|
||
|
||
pDR = new PdfWriter::CResourcesDict(NULL, true, false);
|
||
pDRXref->Add(pDR, nDRxrefGen);
|
||
|
||
pAcroForm->Add(chKey, pDR);
|
||
for (int nIndex2 = 0; nIndex2 < oTemp2.dictGetLength(); ++nIndex2)
|
||
{
|
||
Object oTemp;
|
||
char* chKey2 = oTemp2.dictGetKey(nIndex2);
|
||
oTemp2.dictGetVal(nIndex2, &oTemp);
|
||
DictToCDictObject(&oTemp, pDR, chKey2);
|
||
oTemp.free();
|
||
}
|
||
oTemp2.free();
|
||
|
||
pDR->Fix();
|
||
continue;
|
||
}
|
||
else if (strcmp("Fields", chKey) == 0)
|
||
oAcroForm.dictGetVal(nIndex, &oTemp2);
|
||
else if (strcmp("NeedAppearances", chKey) == 0)
|
||
{
|
||
oTemp2.free();
|
||
continue;
|
||
}
|
||
else
|
||
oAcroForm.dictGetValNF(nIndex, &oTemp2);
|
||
DictToCDictObject(&oTemp2, pAcroForm, chKey);
|
||
oTemp2.free();
|
||
}
|
||
|
||
if (!pAcroForm->Get("Fields"))
|
||
pAcroForm->Add("Fields", new PdfWriter::CArrayObject());
|
||
|
||
oAcroForm.free();
|
||
pCatalog->Add(chKey, pAcroForm);
|
||
continue;
|
||
}
|
||
else
|
||
catDict.dictGetValNF(nIndex, &oAcroForm);
|
||
DictToCDictObject(&oAcroForm, pCatalog, chKey);
|
||
oAcroForm.free();
|
||
}
|
||
catDict.free();
|
||
|
||
// Проверка уникальности имён текущих цифровых подписей pdf
|
||
unsigned int nFormField = 0;
|
||
AcroForm* form = pPDFDocument->getCatalog()->getForm();
|
||
if (form)
|
||
{
|
||
nFormField = form->getNumFields() + 1;
|
||
std::wstring sSig = L"Sig" + std::to_wstring(nFormField);
|
||
int i = 0, nFormFields = form->getNumFields();
|
||
while (i < nFormFields)
|
||
{
|
||
int nLength;
|
||
Unicode* uName = form->getField(i)->getName(&nLength);
|
||
std::wstring sName = NSStringExt::CConverter::GetUnicodeFromUTF32(uName, nLength);
|
||
RELEASEMEM(uName);
|
||
if (sName == sSig)
|
||
{
|
||
i = 0;
|
||
nFormField++;
|
||
sSig = L"Sig" + std::to_wstring(nFormField);
|
||
}
|
||
else
|
||
i++;
|
||
}
|
||
nFormField--;
|
||
}
|
||
|
||
// Получение шифрования из reader и применения для writer
|
||
int nCryptAlgorithm = -1;
|
||
PdfWriter::CEncryptDict* pEncryptDict = NULL;
|
||
if (xref->isEncrypted())
|
||
{
|
||
CryptAlgorithm encAlgorithm;
|
||
GBool ownerPasswordOk;
|
||
int permFlags, keyLength, encVersion;
|
||
xref->getEncryption(&permFlags, &ownerPasswordOk, &keyLength, &encVersion, &encAlgorithm);
|
||
nCryptAlgorithm = encAlgorithm;
|
||
|
||
Object* pTrailerDict = xref->getTrailerDict();
|
||
if (pTrailerDict)
|
||
{
|
||
pEncryptDict = new PdfWriter::CEncryptDict();
|
||
|
||
// Нужно получить словарь Encrypt БЕЗ дешифровки, поэтому времено отключаем encrypted в xref
|
||
xref->offEncrypted();
|
||
|
||
Object encrypt, ID, ID1;
|
||
if (pTrailerDict->dictLookup("Encrypt", &encrypt) && encrypt.isDict())
|
||
{
|
||
for (int nIndex = 0; nIndex < encrypt.dictGetLength(); ++nIndex)
|
||
{
|
||
Object oTemp;
|
||
char* chKey = encrypt.dictGetKey(nIndex);
|
||
encrypt.dictGetValNF(nIndex, &oTemp);
|
||
DictToCDictObject(&oTemp, pEncryptDict, chKey, true);
|
||
oTemp.free();
|
||
}
|
||
}
|
||
|
||
if (!pEncryptDict->Get("Length"))
|
||
pEncryptDict->Add("Length", 40);
|
||
|
||
encrypt.free();
|
||
|
||
if (pTrailerDict->dictLookup("ID", &ID) && ID.isArray() && ID.arrayGet(0, &ID1) && ID1.isString())
|
||
DictToCDictObject(&ID1, pEncryptDict, "ID", true);
|
||
ID.free(); ID1.free();
|
||
|
||
xref->onEncrypted();
|
||
|
||
pEncryptDict->SetRef(0, 0);
|
||
pEncryptDict->Fix();
|
||
|
||
pEncryptDict->SetPasswords(m_wsPassword, m_wsPassword);
|
||
if (!pEncryptDict->UpdateKey(nCryptAlgorithm))
|
||
{
|
||
pagesRefObj.free();
|
||
RELEASEOBJECT(pXref);
|
||
RELEASEOBJECT(pDRXref);
|
||
return false;
|
||
}
|
||
}
|
||
}
|
||
|
||
// Применение редактирования для writer
|
||
bool bRes = pDoc->EditPdf(xref->getLastXRefPos(), xref->getNumObjects() + 1, pXref, pCatalog, pEncryptDict, nFormField);
|
||
if (bRes)
|
||
{
|
||
// Воспроизведение дерева страниц во writer
|
||
GetPageTree(xref, &pagesRefObj);
|
||
|
||
if (pDR && pDRXref)
|
||
bRes = pDoc->EditResources(pDRXref, pDR);
|
||
}
|
||
pagesRefObj.free();
|
||
return bRes;
|
||
}
|
||
void CPdfEditor::Close()
|
||
{
|
||
if (m_wsDstFile.empty())
|
||
return;
|
||
|
||
if (m_nMode == Mode::Unknown)
|
||
{
|
||
if (m_wsDstFile != m_wsSrcFile && NSSystemPath::NormalizePath(m_wsDstFile) != NSSystemPath::NormalizePath(m_wsSrcFile))
|
||
NSFile::CFileBinary::Copy(m_wsSrcFile, m_wsDstFile);
|
||
return;
|
||
}
|
||
if (m_nMode == Mode::Split)
|
||
{
|
||
m_pWriter->SaveToFile(m_wsDstFile);
|
||
return;
|
||
}
|
||
if (m_nMode == Mode::WriteNew)
|
||
{
|
||
PdfWriter::CDocument* pDoc = m_pWriter->GetDocument();
|
||
PdfWriter::CPageTree* pPageTree = pDoc->GetPageTree();
|
||
int nPages = pPageTree->GetCount();
|
||
for (int i = 0; i < nPages; ++i)
|
||
{
|
||
PdfWriter::CObjectBase* pObj = pPageTree->GetObj(i);
|
||
if (pObj && pObj->GetType() != PdfWriter::object_type_DICT)
|
||
EditPage(i, false, true);
|
||
}
|
||
|
||
Object oCatalog;
|
||
PDFDoc* pPDFDocument = m_pReader->GetPDFDocument(0);
|
||
int nStartRefID = 0;
|
||
XRef* xref = pPDFDocument->getXRef();
|
||
if (!xref->getCatalog(&oCatalog)->isDict())
|
||
{
|
||
oCatalog.free();
|
||
return;
|
||
}
|
||
|
||
Object oAcroForm;
|
||
if (oCatalog.dictLookupNF("AcroForm", &oAcroForm)->isRef() || oAcroForm.isDict())
|
||
{
|
||
PdfWriter::CDictObject* pAcroForm = pDoc->GetAcroForm();
|
||
if (!pAcroForm)
|
||
{
|
||
pAcroForm = new PdfWriter::CDictObject();
|
||
if (oAcroForm.isRef())
|
||
pDoc->AddObject(pAcroForm);
|
||
pDoc->SetAcroForm(pAcroForm);
|
||
}
|
||
else
|
||
pAcroForm->Remove("NeedAppearances");
|
||
|
||
if (oAcroForm.isRef())
|
||
{
|
||
oAcroForm.free();
|
||
if (!oCatalog.dictLookup("AcroForm", &oAcroForm)->isDict())
|
||
{
|
||
oAcroForm.free(); oCatalog.free();
|
||
return;
|
||
}
|
||
}
|
||
|
||
for (int nIndex = 0; nIndex < oAcroForm.dictGetLength(); ++nIndex)
|
||
{
|
||
Object oTemp;
|
||
char* chKey = oAcroForm.dictGetKey(nIndex);
|
||
if (strcmp("Fields", chKey) == 0)
|
||
{
|
||
Ref oFieldsRef = { -1, -1 };
|
||
if (oAcroForm.dictGetValNF(nIndex, &oTemp)->isRef())
|
||
oFieldsRef = oTemp.getRef();
|
||
oTemp.free();
|
||
|
||
PdfWriter::CArrayObject* pFields = dynamic_cast<PdfWriter::CArrayObject*>(pAcroForm->Get("Fields"));
|
||
if (!pFields)
|
||
{
|
||
PdfWriter::CObjectBase* pObj = oFieldsRef.num > 0 ? m_mObjManager.GetObj(oFieldsRef.num + nStartRefID) : NULL;
|
||
if (pObj)
|
||
{
|
||
pAcroForm->Add(chKey, pObj);
|
||
m_mObjManager.IncRefCount(oFieldsRef.num + nStartRefID);
|
||
continue;
|
||
}
|
||
}
|
||
|
||
if (oAcroForm.dictGetVal(nIndex, &oTemp)->isArray())
|
||
{
|
||
if (!pFields)
|
||
{
|
||
pFields = new PdfWriter::CArrayObject();
|
||
if (oFieldsRef.num > 0)
|
||
{
|
||
pDoc->AddObject(pFields);
|
||
m_mObjManager.AddObj(oFieldsRef.num + nStartRefID, pFields);
|
||
}
|
||
pAcroForm->Add(chKey, pFields);
|
||
}
|
||
|
||
for (int nIndex = 0; nIndex < oTemp.arrayGetLength(); ++nIndex)
|
||
{
|
||
Object oRes;
|
||
PdfWriter::CObjectBase* pObj = NULL;
|
||
if (oTemp.arrayGetNF(nIndex, &oRes)->isRef())
|
||
pObj = m_mObjManager.GetObj(oRes.getRefNum() + nStartRefID);
|
||
if (pObj)
|
||
{
|
||
pFields->Add(pObj);
|
||
m_mObjManager.IncRefCount(oRes.getRefNum() + nStartRefID);
|
||
AddWidgetParent(pDoc, &m_mObjManager, pObj);
|
||
oRes.free();
|
||
continue;
|
||
}
|
||
oRes.free();
|
||
}
|
||
oTemp.free();
|
||
continue;
|
||
}
|
||
else if (!pFields)
|
||
{
|
||
oTemp.free();
|
||
oAcroForm.dictGetValNF(nIndex, &oTemp);
|
||
}
|
||
else
|
||
{
|
||
oTemp.free();
|
||
continue;
|
||
}
|
||
}
|
||
else if (strcmp("SigFlags", chKey) == 0 || strcmp("XFA", chKey) == 0 || (strcmp("DA", chKey) == 0 && pAcroForm->Get("DA")) || strcmp("NeedAppearances", chKey) == 0)
|
||
{ // Нельзя гарантировать их выполнение
|
||
oTemp.free();
|
||
continue;
|
||
}
|
||
else if (strcmp("DR", chKey) == 0)
|
||
{ // Добавляем только уникальные ключи
|
||
PdfWriter::CDictObject* pDR = dynamic_cast<PdfWriter::CDictObject*>(pAcroForm->Get("DR"));
|
||
if (!pDR)
|
||
{
|
||
pDR = new PdfWriter::CDictObject();
|
||
pDoc->AddObject(pDR);
|
||
pAcroForm->Add(chKey, pDR);
|
||
}
|
||
|
||
PdfWriter::CArrayObject* pProcset = new PdfWriter::CArrayObject();
|
||
pDR->Add("ProcSet", pProcset);
|
||
pProcset->Add(new PdfWriter::CNameObject("PDF"));
|
||
pProcset->Add(new PdfWriter::CNameObject("Text"));
|
||
pProcset->Add(new PdfWriter::CNameObject("ImageB"));
|
||
pProcset->Add(new PdfWriter::CNameObject("ImageC"));
|
||
pProcset->Add(new PdfWriter::CNameObject("ImageI"));
|
||
|
||
if (oAcroForm.dictGetVal(nIndex, &oTemp)->isDict())
|
||
{
|
||
Object oTemp2;
|
||
for (int nIndex2 = 0; nIndex2 < oTemp.dictGetLength(); ++nIndex2)
|
||
{
|
||
char* chKey2 = oTemp.dictGetKey(nIndex2);
|
||
if (strcmp("ProcSet", chKey2) == 0 || !oTemp.dictGetVal(nIndex2, &oTemp2)->isDict())
|
||
{
|
||
oTemp2.free();
|
||
continue;
|
||
}
|
||
PdfWriter::CDictObject* pDict = dynamic_cast<PdfWriter::CDictObject*>(pDR->Get(chKey2));
|
||
if (!pDict)
|
||
{
|
||
Object oTempRef;
|
||
if (oTemp.dictGetValNF(nIndex2, &oTempRef)->isRef())
|
||
{
|
||
PdfWriter::CObjectBase* pObj = m_mObjManager.GetObj(oTempRef.getRefNum() + nStartRefID);
|
||
if (pObj)
|
||
{
|
||
pDR->Add(chKey2, pObj);
|
||
m_mObjManager.IncRefCount(oTempRef.getRefNum() + nStartRefID);
|
||
oTemp2.free(); oTempRef.free();
|
||
continue;
|
||
}
|
||
}
|
||
PdfWriter::CObjectBase* pBase = DictToCDictObject2(&oTemp2, pDoc, xref, &m_mObjManager, nStartRefID);
|
||
if (oTempRef.isRef())
|
||
pDoc->AddObject(pBase);
|
||
pDR->Add(chKey2, pBase);
|
||
oTemp2.free(); oTempRef.free();
|
||
continue;
|
||
}
|
||
else
|
||
{
|
||
for (int nIndex3 = 0; nIndex3 < oTemp2.dictGetLength(); ++nIndex3)
|
||
{
|
||
char* chKey3 = oTemp2.dictGetKey(nIndex3);
|
||
if (pDict->Get(chKey3))
|
||
continue;
|
||
Object oTempRef;
|
||
if (oTemp2.dictGetValNF(nIndex3, &oTempRef)->isRef())
|
||
{
|
||
PdfWriter::CObjectBase* pObj = m_mObjManager.GetObj(oTempRef.getRefNum() + nStartRefID);
|
||
if (pObj)
|
||
{
|
||
pDict->Add(chKey3, pObj);
|
||
m_mObjManager.IncRefCount(oTempRef.getRefNum() + nStartRefID);
|
||
oTemp2.free(); oTempRef.free();
|
||
continue;
|
||
}
|
||
}
|
||
PdfWriter::CObjectBase* pBase = DictToCDictObject2(&oTemp2, pDoc, xref, &m_mObjManager, nStartRefID);
|
||
if (oTempRef.isRef())
|
||
pDoc->AddObject(pBase);
|
||
pDict->Add(chKey3, pBase);
|
||
oTemp2.free(); oTempRef.free();
|
||
continue;
|
||
}
|
||
}
|
||
}
|
||
oTemp2.free(); oTemp.free();
|
||
continue;
|
||
}
|
||
else
|
||
{
|
||
oTemp.free();
|
||
oAcroForm.dictGetValNF(nIndex, &oTemp);
|
||
}
|
||
}
|
||
else
|
||
oAcroForm.dictGetValNF(nIndex, &oTemp);
|
||
PdfWriter::CObjectBase* pBase = DictToCDictObject2(&oTemp, pDoc, xref, &m_mObjManager, nStartRefID);
|
||
pAcroForm->Add(chKey, pBase);
|
||
oTemp.free();
|
||
}
|
||
}
|
||
oAcroForm.free(); oCatalog.free();
|
||
|
||
m_pWriter->SaveToFile(m_wsDstFile);
|
||
return;
|
||
}
|
||
|
||
// m_nMode == Mode::WriteAppend
|
||
PDFDoc* pPDFDocument = m_pReader->GetPDFDocument(0);
|
||
PdfWriter::CDocument* pDoc = m_pWriter->GetDocument();
|
||
XRef* xref = pPDFDocument->getXRef();
|
||
|
||
// Добавляем первый элемент в таблицу xref
|
||
// он должен иметь вид 0000000000 65535 f
|
||
PdfWriter::CXref* pXref = new PdfWriter::CXref(pDoc, 0, 65535);
|
||
if (!pXref)
|
||
return;
|
||
|
||
PdfWriter::CDictObject* pTrailer = NULL;
|
||
Object* trailerDict = xref->getTrailerDict();
|
||
if (trailerDict)
|
||
{
|
||
pTrailer = pXref->GetTrailer();
|
||
|
||
for (int nIndex = 0; nIndex < trailerDict->dictGetLength(); ++nIndex)
|
||
{
|
||
Object oTemp;
|
||
char* chKey = trailerDict->dictGetKey(nIndex);
|
||
trailerDict->dictGetValNF(nIndex, &oTemp);
|
||
DictToCDictObject(&oTemp, pTrailer, chKey);
|
||
oTemp.free();
|
||
}
|
||
}
|
||
|
||
Object info;
|
||
pPDFDocument->getDocInfo(&info);
|
||
PdfWriter::CXref* pInfoXref = NULL;
|
||
PdfWriter::CInfoDict* pInfoDict = NULL;
|
||
if (info.isDict())
|
||
{
|
||
// Обновление Info
|
||
PdfWriter::CObjectBase* pInfo = pTrailer->Get("Info");
|
||
pInfoXref = new PdfWriter::CXref(pDoc, pInfo ? pInfo->GetObjId() : 0);
|
||
if (!pInfoXref)
|
||
{
|
||
RELEASEOBJECT(pXref);
|
||
return;
|
||
}
|
||
pInfoDict = new PdfWriter::CInfoDict();
|
||
if (!pInfoDict)
|
||
{
|
||
RELEASEOBJECT(pXref);
|
||
RELEASEOBJECT(pInfoXref);
|
||
return;
|
||
}
|
||
pInfoXref->Add(pInfoDict, pInfo ? pInfo->GetGenNo() : 0);
|
||
|
||
for (int nIndex = 0; nIndex < info.dictGetLength(); ++nIndex)
|
||
{
|
||
Object oTemp;
|
||
char* chKey = info.dictGetKey(nIndex);
|
||
info.dictGetValNF(nIndex, &oTemp);
|
||
DictToCDictObject(&oTemp, pInfoDict, chKey);
|
||
oTemp.free();
|
||
}
|
||
pInfoDict->SetTime(PdfWriter::InfoModaDate);
|
||
}
|
||
info.free();
|
||
|
||
if (!m_pWriter->EditClose() || !pDoc->AddToFile(m_wsDstFile, pXref, pTrailer, pInfoXref, pInfoDict))
|
||
{
|
||
RELEASEOBJECT(pXref);
|
||
return;
|
||
}
|
||
|
||
std::string sPathUtf8New = U_TO_UTF8(m_wsDstFile);
|
||
std::string sPathUtf8Old = U_TO_UTF8(m_wsSrcFile);
|
||
if (sPathUtf8Old == sPathUtf8New || NSSystemPath::NormalizePath(sPathUtf8Old) == NSSystemPath::NormalizePath(sPathUtf8New))
|
||
{
|
||
GString* owner_pswd = NSStrings::CreateString(m_wsPassword);
|
||
GString* user_pswd = NSStrings::CreateString(m_wsPassword);
|
||
pPDFDocument->makeWritable(false, owner_pswd, user_pswd);
|
||
delete owner_pswd;
|
||
delete user_pswd;
|
||
|
||
NSFile::CFileBinary oFile;
|
||
if (oFile.OpenFile(m_wsSrcFile))
|
||
{
|
||
m_pReader->ChangeLength(oFile.GetFileSize());
|
||
oFile.CloseFile();
|
||
}
|
||
}
|
||
|
||
m_pReader = NULL;
|
||
m_pWriter = NULL;
|
||
m_nEditPage = -1;
|
||
}
|
||
int CPdfEditor::GetError()
|
||
{
|
||
return m_nError;
|
||
}
|
||
void CPdfEditor::GetPageTree(XRef* xref, Object* pPagesRefObj, PdfWriter::CPageTree* pPageParent)
|
||
{
|
||
PdfWriter::CDocument* pDoc = m_pWriter->GetDocument();
|
||
if (!pPagesRefObj || !xref || !pDoc)
|
||
return;
|
||
|
||
Object pagesObj;
|
||
if (!pPagesRefObj->isRef() || !pPagesRefObj->fetch(xref, &pagesObj)->isDict("Pages"))
|
||
{
|
||
pagesObj.free();
|
||
return;
|
||
}
|
||
|
||
Ref topPagesRef = pPagesRefObj->getRef();
|
||
PdfWriter::CXref* pXref = new PdfWriter::CXref(pDoc, topPagesRef.num);
|
||
if (!pXref)
|
||
{
|
||
pagesObj.free();
|
||
return;
|
||
}
|
||
|
||
PdfWriter::CPageTree* pPageT = new PdfWriter::CPageTree();
|
||
if (!pPageT)
|
||
{
|
||
pagesObj.free();
|
||
RELEASEOBJECT(pXref);
|
||
return;
|
||
}
|
||
pXref->Add(pPageT, topPagesRef.gen);
|
||
for (int nIndex = 0; nIndex < pagesObj.dictGetLength(); ++nIndex)
|
||
{
|
||
Object oTemp;
|
||
char* chKey = pagesObj.dictGetKey(nIndex);
|
||
if (strcmp("Resources", chKey) == 0)
|
||
{
|
||
if (pagesObj.dictGetVal(nIndex, &oTemp)->isDict())
|
||
{
|
||
PdfWriter::CResourcesDict* pDict = new PdfWriter::CResourcesDict(NULL, true, false);
|
||
pPageT->Add("Resources", pDict);
|
||
for (int nIndex = 0; nIndex < oTemp.dictGetLength(); ++nIndex)
|
||
{
|
||
Object oRes;
|
||
char* chKey2 = oTemp.dictGetKey(nIndex);
|
||
if (strcmp("Font", chKey2) == 0 || strcmp("ExtGState", chKey2) == 0 || strcmp("XObject", chKey2) == 0 || strcmp("Shading", chKey2) == 0 || strcmp("Pattern", chKey2) == 0)
|
||
oTemp.dictGetVal(nIndex, &oRes);
|
||
else
|
||
oTemp.dictGetValNF(nIndex, &oRes);
|
||
DictToCDictObject(&oRes, pDict, chKey2);
|
||
oRes.free();
|
||
}
|
||
|
||
oTemp.free();
|
||
continue;
|
||
}
|
||
else
|
||
{
|
||
oTemp.free();
|
||
pagesObj.dictGetValNF(nIndex, &oTemp);
|
||
}
|
||
}
|
||
else if (strcmp("Parent", chKey) == 0 && pPageParent)
|
||
{
|
||
pPageT->Add("Parent", pPageParent);
|
||
continue;
|
||
}
|
||
else
|
||
pagesObj.dictGetValNF(nIndex, &oTemp);
|
||
DictToCDictObject(&oTemp, pPageT, chKey);
|
||
oTemp.free();
|
||
}
|
||
pDoc->CreatePageTree(pXref, pPageT);
|
||
pPageT->Fix();
|
||
|
||
Object kidsArrObj;
|
||
if (!pagesObj.dictLookup("Kids", &kidsArrObj)->isArray())
|
||
{
|
||
pagesObj.free();
|
||
kidsArrObj.free();
|
||
return;
|
||
}
|
||
pagesObj.free();
|
||
|
||
for (int i = 0, count = kidsArrObj.arrayGetLength(); i < count; ++i)
|
||
{
|
||
Object kidRefObj;
|
||
if (kidsArrObj.arrayGetNF(i, &kidRefObj))
|
||
GetPageTree(xref, &kidRefObj, pPageT);
|
||
kidRefObj.free();
|
||
}
|
||
kidsArrObj.free();
|
||
}
|
||
bool CPdfEditor::EditPage(int _nPageIndex, bool bSet, bool bActualPos)
|
||
{
|
||
if (m_nMode == Mode::Unknown && !IncrementalUpdates())
|
||
return false;
|
||
|
||
WriteRedact({});
|
||
m_arrRedact.clear();
|
||
|
||
PDFDoc* pPDFDocument = NULL;
|
||
PdfReader::CPdfFontList* pFontList = NULL;
|
||
int nStartRefID = 0;
|
||
PdfWriter::CDocument* pDoc = m_pWriter->GetDocument();
|
||
if (!pDoc)
|
||
return false;
|
||
|
||
PdfWriter::CPageTree* pPageTree = pDoc->GetPageTree();
|
||
PdfWriter::CPage* pEditPage = NULL;
|
||
pEditPage = bActualPos ? pDoc->GetPage(_nPageIndex) : pDoc->GetEditPage(_nPageIndex);
|
||
if (m_nMode == Mode::Split && !pEditPage)
|
||
return false;
|
||
if (pEditPage)
|
||
{
|
||
if (bSet)
|
||
{
|
||
pDoc->SetCurPage(pEditPage);
|
||
m_pWriter->EditPage(pEditPage);
|
||
m_nEditPage = _nPageIndex;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
int nOriginIndex = _nPageIndex;
|
||
if (m_nMode == Mode::WriteNew)
|
||
{
|
||
PdfWriter::CObjectBase* pObj = pPageTree->GetObj(_nPageIndex);
|
||
PdfWriter::CFakePage* pFakePage = NULL;
|
||
if (pObj)
|
||
pFakePage = dynamic_cast<PdfWriter::CFakePage*>(pObj);
|
||
if (pFakePage)
|
||
nOriginIndex = pFakePage->GetOriginIndex();
|
||
}
|
||
int nPageIndex = m_pReader->GetPageIndex(nOriginIndex, &pPDFDocument, &pFontList, &nStartRefID);
|
||
if (nPageIndex < 0 || !pPDFDocument)
|
||
return NULL;
|
||
|
||
if (m_nMode == Mode::WriteNew)
|
||
{
|
||
PdfWriter::CPage* pPage = new PdfWriter::CPage(pDoc);
|
||
pDoc->AddObject(pPage);
|
||
pPageTree->ReplacePage(_nPageIndex, pPage);
|
||
pDoc->AddEditPage(pPage, _nPageIndex);
|
||
|
||
// Получение объекта страницы
|
||
Catalog* pCatalog = pPDFDocument->getCatalog();
|
||
XRef* xref = pPDFDocument->getXRef();
|
||
Ref* pPageRef = pCatalog->getPageRef(nPageIndex);
|
||
Object pageRefObj, pageObj;
|
||
pageRefObj.initRef(pPageRef->num, pPageRef->gen);
|
||
if (!pageRefObj.fetch(xref, &pageObj)->isDict())
|
||
{
|
||
pageObj.free(); pageRefObj.free();
|
||
return false;
|
||
}
|
||
m_mObjManager.AddObj(pPageRef->num + nStartRefID, pPage);
|
||
pageRefObj.free();
|
||
|
||
bool bResources = false, bMediaBox = false, bCropBox = false, bRotate = false;
|
||
for (int nIndex = 0; nIndex < pageObj.dictGetLength(); ++nIndex)
|
||
{
|
||
Object oTemp;
|
||
char* chKey = pageObj.dictGetKey(nIndex);
|
||
if (strcmp("Resources", chKey) == 0)
|
||
{
|
||
bResources = true;
|
||
Ref oResourcesRef = { -1, -1 };
|
||
if (pageObj.dictGetValNF(nIndex, &oTemp)->isRef())
|
||
oResourcesRef = oTemp.getRef();
|
||
oTemp.free();
|
||
|
||
PdfWriter::CObjectBase* pObj = oResourcesRef.num > 0 ? m_mObjManager.GetObj(oResourcesRef.num + nStartRefID) : NULL;
|
||
if (pObj)
|
||
{
|
||
pPage->Add(chKey, pObj);
|
||
m_mObjManager.IncRefCount(oResourcesRef.num + nStartRefID);
|
||
continue;
|
||
}
|
||
|
||
if (pageObj.dictGetVal(nIndex, &oTemp)->isDict())
|
||
{
|
||
PdfWriter::CResourcesDict* pDict = pDoc->CreateResourcesDict(oResourcesRef.num < 0, false);
|
||
if (oResourcesRef.num > 0)
|
||
m_mObjManager.AddObj(oResourcesRef.num + nStartRefID, pDict);
|
||
pPage->Add(chKey, pDict);
|
||
for (int nIndex = 0; nIndex < oTemp.dictGetLength(); ++nIndex)
|
||
{
|
||
Object oRes;
|
||
char* chKey2 = oTemp.dictGetKey(nIndex);
|
||
oTemp.dictGetValNF(nIndex, &oRes);
|
||
PdfWriter::CObjectBase* pBase = DictToCDictObject2(&oRes, pDoc, xref, &m_mObjManager, nStartRefID);
|
||
pDict->Add(chKey2, pBase);
|
||
oRes.free();
|
||
}
|
||
|
||
oTemp.free();
|
||
continue;
|
||
}
|
||
else
|
||
{
|
||
oTemp.free();
|
||
pageObj.dictGetValNF(nIndex, &oTemp);
|
||
}
|
||
}
|
||
else if (strcmp("Parent", chKey) == 0)
|
||
{
|
||
// Поля родителей страниц переносятся к самим страницам
|
||
oTemp.free();
|
||
continue;
|
||
}
|
||
else if (strcmp("MediaBox", chKey) == 0)
|
||
{
|
||
bMediaBox = true;
|
||
pageObj.dictGetValNF(nIndex, &oTemp);
|
||
}
|
||
else if (strcmp("CropBox", chKey) == 0)
|
||
{
|
||
bCropBox = true;
|
||
pageObj.dictGetValNF(nIndex, &oTemp);
|
||
}
|
||
else if (strcmp("Rotate", chKey) == 0)
|
||
{
|
||
bRotate = true;
|
||
pageObj.dictGetValNF(nIndex, &oTemp);
|
||
}
|
||
else if (strcmp("Contents", chKey) == 0)
|
||
{
|
||
pageObj.dictGetValNF(nIndex, &oTemp);
|
||
PdfWriter::CObjectBase* pBase = DictToCDictObject2(&oTemp, pDoc, xref, &m_mObjManager, nStartRefID, 0, false);
|
||
pPage->Add(chKey, pBase);
|
||
continue;
|
||
}
|
||
else
|
||
pageObj.dictGetValNF(nIndex, &oTemp);
|
||
PdfWriter::CObjectBase* pBase = DictToCDictObject2(&oTemp, pDoc, xref, &m_mObjManager, nStartRefID);
|
||
pPage->Add(chKey, pBase);
|
||
oTemp.free();
|
||
}
|
||
if (!bResources || !bMediaBox || !bCropBox || !bRotate)
|
||
{
|
||
Page* pOPage = pCatalog->getPage(nPageIndex);
|
||
if (!bMediaBox)
|
||
{
|
||
PDFRectangle* pRect = pOPage->getMediaBox();
|
||
pPage->Add("MediaBox", PdfWriter::CArrayObject::CreateBox(pRect->x1, pRect->y1, pRect->x2, pRect->y2));
|
||
}
|
||
if (!bCropBox && pOPage->isCropped())
|
||
{
|
||
PDFRectangle* pRect = pOPage->getCropBox();
|
||
pPage->Add("CropBox", PdfWriter::CArrayObject::CreateBox(pRect->x1, pRect->y1, pRect->x2, pRect->y2));
|
||
}
|
||
if (!bRotate)
|
||
pPage->Add("Rotate", pOPage->getRotate());
|
||
if (!bResources)
|
||
{
|
||
Dict* pResources = pOPage->getResourceDict();
|
||
PdfWriter::CResourcesDict* pDict = pDoc->CreateResourcesDict(true, false);
|
||
pPage->Add("Resources", pDict);
|
||
for (int nIndex = 0; nIndex < pResources->getLength(); ++nIndex)
|
||
{
|
||
Object oRes;
|
||
char* chKey2 = pResources->getKey(nIndex);
|
||
pResources->getValNF(nIndex, &oRes);
|
||
PdfWriter::CObjectBase* pBase = DictToCDictObject2(&oRes, pDoc, xref, &m_mObjManager, nStartRefID);
|
||
pDict->Add(chKey2, pBase);
|
||
oRes.free();
|
||
}
|
||
}
|
||
}
|
||
pPage->Fix();
|
||
pDoc->FixEditPage(pPage);
|
||
double dCTM[6] = { 1, 0, 0, 1, 0, 0 };
|
||
GetCTM(xref, &pageObj, dCTM);
|
||
pageObj.free();
|
||
|
||
if (bSet)
|
||
{
|
||
pDoc->SetCurPage(pPage);
|
||
m_pWriter->EditPage(pPage);
|
||
m_nEditPage = _nPageIndex;
|
||
|
||
if (bCropBox)
|
||
{
|
||
Page* pOPage = pCatalog->getPage(nPageIndex);
|
||
if (pOPage->isCropped())
|
||
{
|
||
PDFRectangle* pCropBox = pOPage->getCropBox();
|
||
PdfWriter::CStream* pStream = pPage->GetStream();
|
||
pStream->WriteStr("1 0 0 1 ");
|
||
pStream->WriteReal(pCropBox->x1);
|
||
pStream->WriteChar(' ');
|
||
pStream->WriteReal(pCropBox->y2 - pOPage->getMediaBox()->y2);
|
||
pStream->WriteStr(" cm\012");
|
||
}
|
||
}
|
||
|
||
pPage->StartTransform(dCTM[0], dCTM[1], dCTM[2], dCTM[3], dCTM[4], dCTM[5]);
|
||
pPage->SetStrokeColor(0, 0, 0);
|
||
pPage->SetFillColor(0, 0, 0);
|
||
pPage->SetExtGrState(pDoc->GetExtGState(255, 255));
|
||
pPage->BeginText();
|
||
pPage->SetCharSpace(0);
|
||
pPage->SetTextRenderingMode(PdfWriter::textrenderingmode_Fill);
|
||
pPage->SetHorizontalScaling(100);
|
||
pPage->EndText();
|
||
}
|
||
return true;
|
||
}
|
||
|
||
XRef* xref = pPDFDocument->getXRef();
|
||
Catalog* pCatalog = pPDFDocument->getCatalog();
|
||
if (!xref || !pCatalog)
|
||
return false;
|
||
std::pair<int, int> pPageRef = std::make_pair(0, 0);
|
||
if (bActualPos)
|
||
pPageRef = pDoc->GetPageRef(_nPageIndex);
|
||
else
|
||
{
|
||
Ref* pRef = pPDFDocument->getCatalog()->getPageRef(nPageIndex);
|
||
if (pRef && pRef->num != 0)
|
||
pPageRef = std::make_pair(pRef->num, pRef->gen);
|
||
}
|
||
if (pPageRef.first == 0)
|
||
return false;
|
||
|
||
// Получение объекта страницы
|
||
Object pageRefObj, pageObj;
|
||
pageRefObj.initRef(pPageRef.first, pPageRef.second);
|
||
if (!pageRefObj.fetch(xref, &pageObj) || !pageObj.isDict())
|
||
{
|
||
pageObj.free();
|
||
pageRefObj.free();
|
||
return false;
|
||
}
|
||
pageRefObj.free();
|
||
|
||
// Воспроизведение словаря страницы из reader для writer
|
||
PdfWriter::CXref* pXref = new PdfWriter::CXref(pDoc, pPageRef.first);
|
||
if (!pXref)
|
||
{
|
||
pageObj.free();
|
||
return false;
|
||
}
|
||
PdfWriter::CPage* pPage = new PdfWriter::CPage(pDoc);
|
||
if (!pPage)
|
||
{
|
||
pageObj.free();
|
||
RELEASEOBJECT(pXref);
|
||
return false;
|
||
}
|
||
pXref->Add(pPage, pPageRef.second);
|
||
bool bResources = false, bMediaBox = false, bCropBox = false, bRotate = false;
|
||
for (int nIndex = 0; nIndex < pageObj.dictGetLength(); ++nIndex)
|
||
{
|
||
Object oTemp;
|
||
char* chKey = pageObj.dictGetKey(nIndex);
|
||
if (strcmp("Resources", chKey) == 0)
|
||
{
|
||
bResources = true;
|
||
if (pageObj.dictGetVal(nIndex, &oTemp)->isDict())
|
||
{
|
||
PdfWriter::CResourcesDict* pDict = new PdfWriter::CResourcesDict(NULL, true, false);
|
||
pPage->Add("Resources", pDict);
|
||
for (int nIndex = 0; nIndex < oTemp.dictGetLength(); ++nIndex)
|
||
{
|
||
Object oRes;
|
||
char* chKey2 = oTemp.dictGetKey(nIndex);
|
||
if (strcmp("Font", chKey2) == 0 || strcmp("ExtGState", chKey2) == 0 || strcmp("XObject", chKey2) == 0 || strcmp("Shading", chKey2) == 0 || strcmp("Pattern", chKey2) == 0)
|
||
oTemp.dictGetVal(nIndex, &oRes);
|
||
else
|
||
oTemp.dictGetValNF(nIndex, &oRes);
|
||
DictToCDictObject(&oRes, pDict, chKey2);
|
||
oRes.free();
|
||
}
|
||
|
||
oTemp.free();
|
||
continue;
|
||
}
|
||
else
|
||
{
|
||
oTemp.free();
|
||
pageObj.dictGetValNF(nIndex, &oTemp);
|
||
}
|
||
}
|
||
else if (strcmp("Annots", chKey) == 0)
|
||
{
|
||
if (pageObj.dictGetVal(nIndex, &oTemp)->isArray())
|
||
{
|
||
PdfWriter::CArrayObject* pArray = new PdfWriter::CArrayObject();
|
||
pPage->Add("Annots", pArray);
|
||
for (int nIndex = 0; nIndex < oTemp.arrayGetLength(); ++nIndex)
|
||
{
|
||
Object oAnnot;
|
||
oTemp.arrayGetNF(nIndex, &oAnnot);
|
||
DictToCDictObject(&oAnnot, pArray, "");
|
||
oAnnot.free();
|
||
}
|
||
oTemp.free();
|
||
continue;
|
||
}
|
||
else
|
||
{
|
||
oTemp.free();
|
||
pageObj.dictGetValNF(nIndex, &oTemp);
|
||
}
|
||
}
|
||
else if (strcmp("Contents", chKey) == 0)
|
||
{
|
||
if (pageObj.dictGetVal(nIndex, &oTemp)->isArray())
|
||
{
|
||
DictToCDictObject(&oTemp, pPage, chKey);
|
||
oTemp.free();
|
||
continue;
|
||
}
|
||
else
|
||
{
|
||
oTemp.free();
|
||
pageObj.dictGetValNF(nIndex, &oTemp);
|
||
}
|
||
}
|
||
else if (strcmp("Parent", chKey) == 0)
|
||
{
|
||
pageObj.dictGetValNF(nIndex, &oTemp);
|
||
}
|
||
else if (strcmp("MediaBox", chKey) == 0)
|
||
{
|
||
bMediaBox = true;
|
||
pageObj.dictGetValNF(nIndex, &oTemp);
|
||
}
|
||
else if (strcmp("CropBox", chKey) == 0)
|
||
{
|
||
bCropBox = true;
|
||
pageObj.dictGetValNF(nIndex, &oTemp);
|
||
}
|
||
else if (strcmp("Rotate", chKey) == 0)
|
||
{
|
||
bRotate = true;
|
||
pageObj.dictGetValNF(nIndex, &oTemp);
|
||
}
|
||
else
|
||
pageObj.dictGetValNF(nIndex, &oTemp);
|
||
DictToCDictObject(&oTemp, pPage, chKey);
|
||
oTemp.free();
|
||
}
|
||
if (!bResources || !bMediaBox || !bCropBox || !bRotate)
|
||
{
|
||
Page* pOPage = pCatalog->getPage(nPageIndex);
|
||
if (!bMediaBox)
|
||
{
|
||
PDFRectangle* pRect = pOPage->getMediaBox();
|
||
pPage->Add("MediaBox", PdfWriter::CArrayObject::CreateBox(pRect->x1, pRect->y1, pRect->x2, pRect->y2));
|
||
}
|
||
|
||
if (!bCropBox && pOPage->isCropped())
|
||
{
|
||
PDFRectangle* pRect = pOPage->getCropBox();
|
||
pPage->Add("CropBox", PdfWriter::CArrayObject::CreateBox(pRect->x1, pRect->y1, pRect->x2, pRect->y2));
|
||
}
|
||
|
||
if (!bRotate)
|
||
pPage->Add("Rotate", pOPage->getRotate());
|
||
|
||
if (!bResources)
|
||
{
|
||
Dict* pResources = pOPage->getResourceDict();
|
||
PdfWriter::CResourcesDict* pDict = pDoc->CreateResourcesDict(true, false);
|
||
pPage->Add("Resources", pDict);
|
||
for (int nIndex = 0; nIndex < pResources->getLength(); ++nIndex)
|
||
{
|
||
Object oRes;
|
||
char* chKey2 = pResources->getKey(nIndex);
|
||
if (strcmp("Font", chKey2) == 0 || strcmp("ExtGState", chKey2) == 0 || strcmp("XObject", chKey2) == 0 || strcmp("Shading", chKey2) == 0 || strcmp("Pattern", chKey2) == 0)
|
||
pResources->getVal(nIndex, &oRes);
|
||
else
|
||
pResources->getValNF(nIndex, &oRes);
|
||
DictToCDictObject(&oRes, pDict, chKey2);
|
||
}
|
||
}
|
||
}
|
||
pPage->Fix();
|
||
double dCTM[6] = { 1, 0, 0, 1, 0, 0 };
|
||
GetCTM(xref, &pageObj, dCTM);
|
||
pageObj.free();
|
||
|
||
// Применение редактирования страницы для writer
|
||
if (pDoc->EditPage(pXref, pPage, _nPageIndex))
|
||
{
|
||
if (bSet)
|
||
{
|
||
m_pWriter->EditPage(pPage);
|
||
m_nEditPage = _nPageIndex;
|
||
}
|
||
if (bCropBox)
|
||
{
|
||
Page* pOPage = pCatalog->getPage(nPageIndex);
|
||
if (pOPage->isCropped())
|
||
{
|
||
PDFRectangle* pCropBox = pOPage->getCropBox();
|
||
PdfWriter::CStream* pStream = pPage->GetStream();
|
||
pStream->WriteStr("1 0 0 1 ");
|
||
pStream->WriteReal(pCropBox->x1);
|
||
pStream->WriteChar(' ');
|
||
pStream->WriteReal(pCropBox->y2 - pOPage->getMediaBox()->y2);
|
||
pStream->WriteStr(" cm\012");
|
||
}
|
||
}
|
||
pPage->StartTransform(dCTM[0], dCTM[1], dCTM[2], dCTM[3], dCTM[4], dCTM[5]);
|
||
pPage->SetStrokeColor(0, 0, 0);
|
||
pPage->SetFillColor(0, 0, 0);
|
||
pPage->SetExtGrState(pDoc->GetExtGState(255, 255));
|
||
pPage->BeginText();
|
||
pPage->SetCharSpace(0);
|
||
pPage->SetTextRenderingMode(PdfWriter::textrenderingmode_Fill);
|
||
pPage->SetHorizontalScaling(100);
|
||
pPage->EndText();
|
||
return true;
|
||
}
|
||
|
||
RELEASEOBJECT(pXref);
|
||
return false;
|
||
}
|
||
bool CPdfEditor::SplitPages(const int* arrPageIndex, unsigned int unLength, PDFDoc* _pDoc, int nStartRefID)
|
||
{
|
||
if (m_nMode == Mode::Unknown)
|
||
return false;
|
||
PDFDoc* pPDFDocument = _pDoc;
|
||
XRef* xref = pPDFDocument->getXRef();
|
||
PdfWriter::CDocument* pDoc = m_pWriter->GetDocument();
|
||
PdfWriter::CPageTree* pPageTree = pDoc->GetPageTree();
|
||
int nPagesBefore = m_pReader->GetNumPagesBefore(pPDFDocument);
|
||
|
||
if (unLength == 0)
|
||
unLength = pPDFDocument->getNumPages();
|
||
|
||
// Страницы должны быть созданы заранее для ссылки на них
|
||
Catalog* pCatalog = pPDFDocument->getCatalog();
|
||
for (unsigned int i = 0; i < unLength; ++i)
|
||
{
|
||
Ref* pPageRef = pCatalog->getPageRef((arrPageIndex ? arrPageIndex[i] : i) + 1);
|
||
if (pPageRef->num == 0)
|
||
return false;
|
||
|
||
PdfWriter::CPage* pPage = new PdfWriter::CPage(pDoc);
|
||
pDoc->AddObject(pPage);
|
||
if (m_nMode == Mode::WriteAppend || m_nMode == Mode::WriteNew)
|
||
pDoc->AddPage(pDoc->GetPagesCount(), pPage);
|
||
else
|
||
pPageTree->ReplacePage(nPagesBefore + (arrPageIndex ? arrPageIndex[i] : i), pPage);
|
||
pDoc->AddEditPage(pPage, nPagesBefore + (arrPageIndex ? arrPageIndex[i] : i));
|
||
|
||
// Получение объекта страницы
|
||
Object pageRefObj, pageObj;
|
||
pageRefObj.initRef(pPageRef->num, pPageRef->gen);
|
||
if (!pageRefObj.fetch(xref, &pageObj)->isDict())
|
||
{
|
||
pageObj.free(); pageRefObj.free();
|
||
return false;
|
||
}
|
||
m_mObjManager.AddObj(pPageRef->num + nStartRefID, pPage);
|
||
pageObj.free(); pageRefObj.free();
|
||
}
|
||
|
||
for (unsigned int i = 0; i < unLength; ++i)
|
||
{
|
||
Ref* pPageRef = pCatalog->getPageRef((arrPageIndex ? arrPageIndex[i] : i) + 1);
|
||
if (pPageRef->num == 0)
|
||
return false;
|
||
|
||
// Получение объекта страницы
|
||
PdfWriter::CPage* pPage = (PdfWriter::CPage*)m_mObjManager.GetObj(pPageRef->num + nStartRefID);
|
||
Object pageRefObj, pageObj;
|
||
pageRefObj.initRef(pPageRef->num, pPageRef->gen);
|
||
if (!pageRefObj.fetch(xref, &pageObj)->isDict())
|
||
{
|
||
pageObj.free();
|
||
pageRefObj.free();
|
||
return false;
|
||
}
|
||
pageRefObj.free();
|
||
|
||
bool bResources = false, bMediaBox = false, bCropBox = false, bRotate = false;
|
||
// Копирование страницы со всеми ресурсами из reader для writer
|
||
for (int nIndex = 0; nIndex < pageObj.dictGetLength(); ++nIndex)
|
||
{
|
||
Object oTemp;
|
||
char* chKey = pageObj.dictGetKey(nIndex);
|
||
if (strcmp("Resources", chKey) == 0)
|
||
{
|
||
bResources = true;
|
||
Ref oResourcesRef = { -1, -1 };
|
||
if (pageObj.dictGetValNF(nIndex, &oTemp)->isRef())
|
||
oResourcesRef = oTemp.getRef();
|
||
oTemp.free();
|
||
|
||
PdfWriter::CObjectBase* pObj = oResourcesRef.num > 0 ? m_mObjManager.GetObj(oResourcesRef.num + nStartRefID) : NULL;
|
||
if (pObj)
|
||
{
|
||
pPage->Add(chKey, pObj);
|
||
m_mObjManager.IncRefCount(oResourcesRef.num + nStartRefID);
|
||
continue;
|
||
}
|
||
|
||
if (pageObj.dictGetVal(nIndex, &oTemp)->isDict())
|
||
{
|
||
PdfWriter::CResourcesDict* pDict = pDoc->CreateResourcesDict(oResourcesRef.num < 0, false);
|
||
if (oResourcesRef.num > 0)
|
||
m_mObjManager.AddObj(oResourcesRef.num + nStartRefID, pDict);
|
||
pPage->Add(chKey, pDict);
|
||
for (int nIndex = 0; nIndex < oTemp.dictGetLength(); ++nIndex)
|
||
{
|
||
Object oRes;
|
||
char* chKey2 = oTemp.dictGetKey(nIndex);
|
||
oTemp.dictGetValNF(nIndex, &oRes);
|
||
PdfWriter::CObjectBase* pBase = DictToCDictObject2(&oRes, pDoc, xref, &m_mObjManager, nStartRefID);
|
||
pDict->Add(chKey2, pBase);
|
||
oRes.free();
|
||
}
|
||
|
||
oTemp.free();
|
||
continue;
|
||
}
|
||
else
|
||
{
|
||
oTemp.free();
|
||
pageObj.dictGetValNF(nIndex, &oTemp);
|
||
}
|
||
}
|
||
else if (strcmp("Parent", chKey) == 0)
|
||
{
|
||
oTemp.free();
|
||
continue;
|
||
}
|
||
else if (strcmp("MediaBox", chKey) == 0)
|
||
{
|
||
bMediaBox = true;
|
||
pageObj.dictGetValNF(nIndex, &oTemp);
|
||
}
|
||
else if (strcmp("CropBox", chKey) == 0)
|
||
{
|
||
bCropBox = true;
|
||
pageObj.dictGetValNF(nIndex, &oTemp);
|
||
}
|
||
else if (strcmp("Rotate", chKey) == 0)
|
||
{
|
||
bRotate = true;
|
||
pageObj.dictGetValNF(nIndex, &oTemp);
|
||
}
|
||
else
|
||
pageObj.dictGetValNF(nIndex, &oTemp);
|
||
PdfWriter::CObjectBase* pBase = DictToCDictObject2(&oTemp, pDoc, xref, &m_mObjManager, nStartRefID);
|
||
pPage->Add(chKey, pBase);
|
||
if (strcmp("Contents", chKey) == 0)
|
||
{
|
||
if (pBase->GetType() == PdfWriter::object_type_ARRAY)
|
||
{
|
||
PdfWriter::CArrayObject* pArr = (PdfWriter::CArrayObject*)pBase;
|
||
for (int j = 0; j < pArr->GetCount(); ++j)
|
||
{
|
||
pBase = pArr->Get(j);
|
||
if (pBase->GetType() == PdfWriter::object_type_DICT)
|
||
{
|
||
PdfWriter::CDictObject* pDict = (PdfWriter::CDictObject*)pBase;
|
||
if (pDict->Get("Filter"))
|
||
pDict->SetFilter(STREAM_FILTER_ALREADY_DECODE);
|
||
}
|
||
}
|
||
}
|
||
else if (pBase->GetType() == PdfWriter::object_type_DICT)
|
||
{
|
||
PdfWriter::CDictObject* pDict = (PdfWriter::CDictObject*)pBase;
|
||
if (pDict->Get("Filter"))
|
||
pDict->SetFilter(STREAM_FILTER_ALREADY_DECODE);
|
||
}
|
||
}
|
||
oTemp.free();
|
||
}
|
||
if (!bResources || !bMediaBox || !bCropBox || !bRotate)
|
||
{
|
||
Page* pOPage = pCatalog->getPage((arrPageIndex ? arrPageIndex[i] : i) + 1);
|
||
if (!bMediaBox)
|
||
{
|
||
PDFRectangle* pRect = pOPage->getMediaBox();
|
||
pPage->Add("MediaBox", PdfWriter::CArrayObject::CreateBox(pRect->x1, pRect->y1, pRect->x2, pRect->y2));
|
||
}
|
||
if (!bCropBox && pOPage->isCropped())
|
||
{
|
||
PDFRectangle* pRect = pOPage->getCropBox();
|
||
pPage->Add("CropBox", PdfWriter::CArrayObject::CreateBox(pRect->x1, pRect->y1, pRect->x2, pRect->y2));
|
||
}
|
||
if (!bRotate)
|
||
pPage->Add("Rotate", pOPage->getRotate());
|
||
if (!bResources)
|
||
{
|
||
Dict* pResources = pOPage->getResourceDict();
|
||
PdfWriter::CResourcesDict* pDict = pDoc->CreateResourcesDict(true, false);
|
||
pPage->Add("Resources", pDict);
|
||
for (int nIndex = 0; nIndex < pResources->getLength(); ++nIndex)
|
||
{
|
||
Object oRes;
|
||
char* chKey2 = pResources->getKey(nIndex);
|
||
pResources->getValNF(nIndex, &oRes);
|
||
PdfWriter::CObjectBase* pBase = DictToCDictObject2(&oRes, pDoc, xref, &m_mObjManager, nStartRefID);
|
||
pDict->Add(chKey2, pBase);
|
||
oRes.free();
|
||
}
|
||
}
|
||
}
|
||
pPage->Fix();
|
||
if (m_nMode == Mode::WriteAppend || m_nMode == Mode::WriteNew)
|
||
{
|
||
pDoc->FixEditPage(pPage);
|
||
|
||
double dCTM[6] = { 1, 0, 0, 1, 0, 0 };
|
||
GetCTM(xref, &pageObj, dCTM);
|
||
pPage->StartTransform(dCTM[0], dCTM[1], dCTM[2], dCTM[3], dCTM[4], dCTM[5]);
|
||
pPage->SetStrokeColor(0, 0, 0);
|
||
pPage->SetFillColor(0, 0, 0);
|
||
pPage->SetExtGrState(pDoc->GetExtGState(255, 255));
|
||
pPage->BeginText();
|
||
pPage->SetCharSpace(0);
|
||
pPage->SetTextRenderingMode(PdfWriter::textrenderingmode_Fill);
|
||
pPage->SetHorizontalScaling(100);
|
||
pPage->EndText();
|
||
}
|
||
else
|
||
m_pWriter->SetNeedAddHelvetica(false); // TODO дописывает шрифт для адекватного редактирования Adobe pdf без текст. Убрать при реализации map шрифтов
|
||
pageObj.free();
|
||
}
|
||
|
||
Object oCatalog;
|
||
if (!xref->getCatalog(&oCatalog)->isDict())
|
||
{
|
||
oCatalog.free();
|
||
return false;
|
||
}
|
||
|
||
Object oAcroForm;
|
||
if (oCatalog.dictLookupNF("AcroForm", &oAcroForm)->isRef() || oAcroForm.isDict())
|
||
{
|
||
PdfWriter::CDictObject* pAcroForm = pDoc->GetAcroForm();
|
||
if (!pAcroForm)
|
||
{
|
||
pAcroForm = new PdfWriter::CDictObject();
|
||
if (oAcroForm.isRef())
|
||
pDoc->AddObject(pAcroForm);
|
||
pDoc->SetAcroForm(pAcroForm);
|
||
}
|
||
else
|
||
pAcroForm->Remove("NeedAppearances");
|
||
|
||
if (oAcroForm.isRef())
|
||
{
|
||
oAcroForm.free();
|
||
if (!oCatalog.dictLookup("AcroForm", &oAcroForm)->isDict())
|
||
{
|
||
oAcroForm.free(); oCatalog.free();
|
||
return false;
|
||
}
|
||
}
|
||
|
||
for (int nIndex = 0; nIndex < oAcroForm.dictGetLength(); ++nIndex)
|
||
{
|
||
Object oTemp;
|
||
char* chKey = oAcroForm.dictGetKey(nIndex);
|
||
if (strcmp("Fields", chKey) == 0)
|
||
{
|
||
Ref oFieldsRef = { -1, -1 };
|
||
if (oAcroForm.dictGetValNF(nIndex, &oTemp)->isRef())
|
||
oFieldsRef = oTemp.getRef();
|
||
oTemp.free();
|
||
|
||
PdfWriter::CArrayObject* pFields = dynamic_cast<PdfWriter::CArrayObject*>(pAcroForm->Get("Fields"));
|
||
if (!pFields)
|
||
{
|
||
PdfWriter::CObjectBase* pObj = oFieldsRef.num > 0 ? m_mObjManager.GetObj(oFieldsRef.num + nStartRefID) : NULL;
|
||
if (pObj)
|
||
{
|
||
pAcroForm->Add(chKey, pObj);
|
||
m_mObjManager.IncRefCount(oFieldsRef.num + nStartRefID);
|
||
continue;
|
||
}
|
||
}
|
||
|
||
if (oAcroForm.dictGetVal(nIndex, &oTemp)->isArray())
|
||
{
|
||
if (!pFields)
|
||
{
|
||
pFields = new PdfWriter::CArrayObject();
|
||
if (oFieldsRef.num > 0)
|
||
{
|
||
pDoc->AddObject(pFields);
|
||
m_mObjManager.AddObj(oFieldsRef.num + nStartRefID, pFields);
|
||
}
|
||
pAcroForm->Add(chKey, pFields);
|
||
}
|
||
|
||
for (int nIndex = 0; nIndex < oTemp.arrayGetLength(); ++nIndex)
|
||
{
|
||
Object oRes;
|
||
PdfWriter::CObjectBase* pObj = NULL;
|
||
if (oTemp.arrayGetNF(nIndex, &oRes)->isRef())
|
||
pObj = m_mObjManager.GetObj(oRes.getRefNum() + nStartRefID);
|
||
if (pObj)
|
||
{
|
||
pFields->Add(pObj);
|
||
m_mObjManager.IncRefCount(oRes.getRefNum() + nStartRefID);
|
||
AddWidgetParent(pDoc, &m_mObjManager, pObj);
|
||
oRes.free();
|
||
continue;
|
||
}
|
||
oRes.free();
|
||
}
|
||
oTemp.free();
|
||
continue;
|
||
}
|
||
else if (!pFields)
|
||
{
|
||
oTemp.free();
|
||
oAcroForm.dictGetValNF(nIndex, &oTemp);
|
||
}
|
||
else
|
||
{
|
||
oTemp.free();
|
||
continue;
|
||
}
|
||
}
|
||
else if (strcmp("SigFlags", chKey) == 0 || strcmp("XFA", chKey) == 0 || (strcmp("DA", chKey) == 0 && pAcroForm->Get("DA")) || strcmp("NeedAppearances", chKey) == 0)
|
||
{ // Нельзя гарантировать их выполнение
|
||
oTemp.free();
|
||
continue;
|
||
}
|
||
else if (strcmp("DR", chKey) == 0)
|
||
{ // Добавляем только уникальные ключи
|
||
PdfWriter::CDictObject* pDR = dynamic_cast<PdfWriter::CDictObject*>(pAcroForm->Get("DR"));
|
||
if (!pDR)
|
||
{
|
||
pDR = new PdfWriter::CDictObject();
|
||
pDoc->AddObject(pDR);
|
||
pAcroForm->Add(chKey, pDR);
|
||
}
|
||
|
||
PdfWriter::CArrayObject* pProcset = new PdfWriter::CArrayObject();
|
||
pDR->Add("ProcSet", pProcset);
|
||
pProcset->Add(new PdfWriter::CNameObject("PDF"));
|
||
pProcset->Add(new PdfWriter::CNameObject("Text"));
|
||
pProcset->Add(new PdfWriter::CNameObject("ImageB"));
|
||
pProcset->Add(new PdfWriter::CNameObject("ImageC"));
|
||
pProcset->Add(new PdfWriter::CNameObject("ImageI"));
|
||
|
||
if (oAcroForm.dictGetVal(nIndex, &oTemp)->isDict())
|
||
{
|
||
Object oTemp2;
|
||
for (int nIndex2 = 0; nIndex2 < oTemp.dictGetLength(); ++nIndex2)
|
||
{
|
||
char* chKey2 = oTemp.dictGetKey(nIndex2);
|
||
if (strcmp("ProcSet", chKey2) == 0 || !oTemp.dictGetVal(nIndex2, &oTemp2)->isDict())
|
||
{
|
||
oTemp2.free();
|
||
continue;
|
||
}
|
||
PdfWriter::CDictObject* pDict = dynamic_cast<PdfWriter::CDictObject*>(pDR->Get(chKey2));
|
||
if (!pDict)
|
||
{
|
||
Object oTempRef;
|
||
if (oTemp.dictGetValNF(nIndex2, &oTempRef)->isRef())
|
||
{
|
||
PdfWriter::CObjectBase* pObj = m_mObjManager.GetObj(oTempRef.getRefNum() + nStartRefID);
|
||
if (pObj)
|
||
{
|
||
pDR->Add(chKey2, pObj);
|
||
m_mObjManager.IncRefCount(oTempRef.getRefNum() + nStartRefID);
|
||
oTemp2.free(); oTempRef.free();
|
||
continue;
|
||
}
|
||
}
|
||
PdfWriter::CObjectBase* pBase = DictToCDictObject2(&oTemp2, pDoc, xref, &m_mObjManager, nStartRefID);
|
||
if (oTempRef.isRef())
|
||
pDoc->AddObject(pBase);
|
||
pDR->Add(chKey2, pBase);
|
||
oTemp2.free(); oTempRef.free();
|
||
continue;
|
||
}
|
||
else
|
||
{
|
||
for (int nIndex3 = 0; nIndex3 < oTemp2.dictGetLength(); ++nIndex3)
|
||
{
|
||
char* chKey3 = oTemp2.dictGetKey(nIndex3);
|
||
if (pDict->Get(chKey3))
|
||
continue;
|
||
Object oTempRef;
|
||
if (oTemp2.dictGetValNF(nIndex3, &oTempRef)->isRef())
|
||
{
|
||
PdfWriter::CObjectBase* pObj = m_mObjManager.GetObj(oTempRef.getRefNum() + nStartRefID);
|
||
if (pObj)
|
||
{
|
||
pDict->Add(chKey3, pObj);
|
||
m_mObjManager.IncRefCount(oTempRef.getRefNum() + nStartRefID);
|
||
oTemp2.free(); oTempRef.free();
|
||
continue;
|
||
}
|
||
}
|
||
PdfWriter::CObjectBase* pBase = DictToCDictObject2(&oTemp2, pDoc, xref, &m_mObjManager, nStartRefID);
|
||
if (oTempRef.isRef())
|
||
pDoc->AddObject(pBase);
|
||
pDict->Add(chKey3, pBase);
|
||
oTemp2.free(); oTempRef.free();
|
||
continue;
|
||
}
|
||
}
|
||
}
|
||
oTemp2.free(); oTemp.free();
|
||
continue;
|
||
}
|
||
else
|
||
{
|
||
oTemp.free();
|
||
oAcroForm.dictGetValNF(nIndex, &oTemp);
|
||
}
|
||
}
|
||
else
|
||
oAcroForm.dictGetValNF(nIndex, &oTemp);
|
||
PdfWriter::CObjectBase* pBase = DictToCDictObject2(&oTemp, pDoc, xref, &m_mObjManager, nStartRefID);
|
||
pAcroForm->Add(chKey, pBase);
|
||
oTemp.free();
|
||
}
|
||
}
|
||
oAcroForm.free(); oCatalog.free();
|
||
|
||
// Переименование полей
|
||
std::string sPrefix = m_pReader->GetPrefixForm(pPDFDocument);
|
||
if (!sPrefix.empty())
|
||
{
|
||
sPrefix = "_" + sPrefix;
|
||
std::vector<int> arrRename; // Вектор переименованных полей
|
||
std::map<int, PdfWriter::CAnnotation*> mAnnots = m_pWriter->GetDocument()->GetAnnots();
|
||
for (auto it = mAnnots.begin(); it != mAnnots.end(); it++)
|
||
{
|
||
PdfWriter::CAnnotation* pAnnot = it->second;
|
||
if (pAnnot->GetAnnotationType() != PdfWriter::AnnotWidget || it->first < nStartRefID)
|
||
continue;
|
||
|
||
std::vector<int>::iterator it2 = std::find(arrRename.begin(), arrRename.end(), it->first);
|
||
if (it2 != arrRename.end())
|
||
continue;
|
||
|
||
PdfWriter::CObjectBase* pObjBase = pAnnot->Get("Parent");
|
||
if (!pObjBase || !ChangeFullNameParent(m_mObjManager.FindObj(pObjBase), sPrefix, arrRename))
|
||
{
|
||
pObjBase = pAnnot->Get("T");
|
||
if (pObjBase && pObjBase->GetType() == PdfWriter::object_type_STRING)
|
||
{
|
||
PdfWriter::CStringObject* pStr = (PdfWriter::CStringObject*)pObjBase;
|
||
pStr->Add(sPrefix.c_str());
|
||
}
|
||
else if (pObjBase && pObjBase->GetType() == PdfWriter::object_type_BINARY)
|
||
{
|
||
PdfWriter::CBinaryObject* pBin = (PdfWriter::CBinaryObject*)pObjBase;
|
||
pBin->Add((BYTE*)sPrefix.c_str(), sPrefix.length());
|
||
}
|
||
}
|
||
arrRename.push_back(it->first);
|
||
}
|
||
}
|
||
|
||
return true;
|
||
}
|
||
bool CPdfEditor::SplitPages(const int* arrPageIndex, unsigned int unLength)
|
||
{
|
||
if (m_nMode != Mode::Split)
|
||
return false;
|
||
PdfWriter::CDocument* pDoc = m_pWriter->GetDocument();
|
||
if (!pDoc)
|
||
return false;
|
||
PdfWriter::CPageTree* pPageTree = pDoc->GetPageTree();
|
||
if (!pPageTree)
|
||
return false;
|
||
|
||
m_mObjManager.SetDoc(pDoc);
|
||
int nPages = m_pReader->GetNumPages();
|
||
pPageTree->CreateFakePages(nPages);
|
||
|
||
int nTotalPages = 0;
|
||
int nPDFIndex = 0;
|
||
std::map<int, std::vector<int>> mFileToPages;
|
||
PDFDoc* pPDFDocument = m_pReader->GetPDFDocument(nPDFIndex);
|
||
nPages = pPDFDocument->getNumPages();
|
||
for (unsigned int i = 0; i < unLength; ++i)
|
||
{
|
||
if (arrPageIndex[i] < nTotalPages + nPages)
|
||
mFileToPages[nPDFIndex].push_back(arrPageIndex[i] - nTotalPages);
|
||
else
|
||
{
|
||
pPDFDocument = m_pReader->GetPDFDocument(++nPDFIndex);
|
||
if (!pPDFDocument)
|
||
{
|
||
m_mObjManager.m_arrSplitAddPages.push_back(arrPageIndex[i]);
|
||
--nPDFIndex;
|
||
continue;
|
||
}
|
||
nTotalPages += nPages;
|
||
nPages = pPDFDocument->getNumPages();
|
||
--i;
|
||
}
|
||
}
|
||
|
||
for (const std::pair<const int, std::vector<int>>& it : mFileToPages)
|
||
{
|
||
pPDFDocument = m_pReader->GetPDFDocument(it.first);
|
||
if (!SplitPages(it.second.data(), it.second.size(), pPDFDocument, m_pReader->GetStartRefID(pPDFDocument)))
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
void CPdfEditor::AfterSplitPages()
|
||
{
|
||
PdfWriter::CDocument* pDoc = m_pWriter->GetDocument();
|
||
if (!pDoc)
|
||
return;
|
||
PdfWriter::CPageTree* pPageTree = pDoc->GetPageTree();
|
||
if (!pPageTree)
|
||
return;
|
||
pPageTree->ClearFakePages();
|
||
}
|
||
void CreateOutlines(PDFDoc* pdfDoc, PdfWriter::CDocument* pDoc, OutlineItem* pOutlineItem, PdfWriter::COutline* pParent)
|
||
{
|
||
std::string sTitle = NSStringExt::CConverter::GetUtf8FromUTF32(pOutlineItem->getTitle(), pOutlineItem->getTitleLength());
|
||
PdfWriter::COutline* pOutline = pDoc->CreateOutline(pParent, sTitle.c_str());
|
||
|
||
PdfWriter::CDestination* pDest = NULL;
|
||
LinkAction* pLinkAction = pOutlineItem->getAction();
|
||
if (pLinkAction && pLinkAction->getKind() == actionGoTo)
|
||
{
|
||
GString* str = ((LinkGoTo*)pLinkAction)->getNamedDest();
|
||
LinkDest* pLinkDest = str ? pdfDoc->findDest(str) : ((LinkGoTo*)pLinkAction)->getDest();
|
||
if (pLinkDest)
|
||
{
|
||
int pg;
|
||
if (pLinkDest->isPageRef())
|
||
{
|
||
Ref pageRef = pLinkDest->getPageRef();
|
||
pg = pdfDoc->findPage(pageRef.num, pageRef.gen);
|
||
}
|
||
else
|
||
pg = pLinkDest->getPageNum();
|
||
if (pg == 0)
|
||
pg = 1;
|
||
|
||
Ref* pPageRef = pdfDoc->getCatalog()->getPageRef(pg);
|
||
PdfWriter::CObjectBase* pPageD = pDoc->GetEditPage(--pg);
|
||
if (!pPageD && pPageRef->num > 0)
|
||
{
|
||
PdfWriter::CObjectBase* pBase = new PdfWriter::CObjectBase();
|
||
pBase->SetRef(pPageRef->num, pPageRef->gen);
|
||
pPageD = new PdfWriter::CProxyObject(pBase, true);
|
||
}
|
||
pDest = pDoc->CreateDestination(pPageD, true);
|
||
|
||
switch (pLinkDest->getKind())
|
||
{
|
||
case destXYZ:
|
||
{
|
||
pDest->SetXYZ(pLinkDest->getLeft(), pLinkDest->getTop(), pLinkDest->getZoom());
|
||
break;
|
||
}
|
||
case destFit:
|
||
{
|
||
pDest->SetFit();
|
||
break;
|
||
}
|
||
case destFitH:
|
||
{
|
||
pDest->SetFitH(pLinkDest->getTop());
|
||
break;
|
||
}
|
||
case destFitV:
|
||
{
|
||
pDest->SetFitV(pLinkDest->getLeft());
|
||
break;
|
||
}
|
||
case destFitR:
|
||
{
|
||
pDest->SetFitR(pLinkDest->getLeft(), pLinkDest->getBottom(), pLinkDest->getRight(), pLinkDest->getTop());
|
||
break;
|
||
}
|
||
case destFitB:
|
||
{
|
||
pDest->SetFitB();
|
||
break;
|
||
}
|
||
case destFitBH:
|
||
{
|
||
pDest->SetFitBH(pLinkDest->getTop());
|
||
break;
|
||
}
|
||
case destFitBV:
|
||
{
|
||
pDest->SetFitBV(pLinkDest->getLeft());
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
if (str)
|
||
RELEASEOBJECT(pLinkDest);
|
||
}
|
||
if (pDest)
|
||
pOutline->SetDestination(pDest);
|
||
|
||
pOutlineItem->open();
|
||
GList* pList = pOutlineItem->getKids();
|
||
if (!pList)
|
||
{
|
||
pOutlineItem->close();
|
||
return;
|
||
}
|
||
for (int i = 0, num = pList->getLength(); i < num; i++)
|
||
{
|
||
OutlineItem* pOutlineItemKid = (OutlineItem*)pList->get(i);
|
||
if (pOutlineItemKid)
|
||
CreateOutlines(pdfDoc, pDoc, pOutlineItemKid, pOutline);
|
||
}
|
||
pOutlineItem->close();
|
||
}
|
||
bool CPdfEditor::ChangeFullNameParent(int nParent, const std::string& sPrefix, std::vector<int>& arrRename)
|
||
{
|
||
std::vector<int>::const_iterator it2 = std::find(arrRename.begin(), arrRename.end(), nParent);
|
||
if (it2 != arrRename.end())
|
||
return true;
|
||
|
||
PdfWriter::CObjectBase* pObjBase = m_mObjManager.GetObj(nParent);
|
||
if (!pObjBase || pObjBase->GetType() != PdfWriter::object_type_DICT)
|
||
return false;
|
||
|
||
PdfWriter::CDictObject* pDict = (PdfWriter::CDictObject*)pObjBase;
|
||
pObjBase = pDict->Get("Parent");
|
||
if (!pObjBase || !ChangeFullNameParent(m_mObjManager.FindObj(pObjBase), sPrefix, arrRename))
|
||
{
|
||
pObjBase = pDict->Get("T");
|
||
if (pObjBase && pObjBase->GetType() == PdfWriter::object_type_STRING)
|
||
{
|
||
PdfWriter::CStringObject* pStr = (PdfWriter::CStringObject*)pObjBase;
|
||
pStr->Add(sPrefix.c_str());
|
||
}
|
||
else if (pObjBase && pObjBase->GetType() == PdfWriter::object_type_BINARY)
|
||
{
|
||
PdfWriter::CBinaryObject* pBin = (PdfWriter::CBinaryObject*)pObjBase;
|
||
pBin->Add((BYTE*)sPrefix.c_str(), sPrefix.length());
|
||
}
|
||
}
|
||
arrRename.push_back(nParent);
|
||
|
||
return true;
|
||
}
|
||
bool CPdfEditor::MergePages(const std::wstring& wsPath)
|
||
{
|
||
if (m_nMode == Mode::Unknown && !IncrementalUpdates())
|
||
return false;
|
||
m_nOriginIndex = m_pReader->GetNumPages();
|
||
PDFDoc* pDocument = m_pReader->GetLastPDFDocument();
|
||
int nStartRefID = m_pReader->GetStartRefID(pDocument);
|
||
m_mObjManager.SetDoc(m_pWriter->GetDocument());
|
||
bool bRes = SplitPages(NULL, 0, pDocument, nStartRefID);
|
||
if (!bRes)
|
||
return false;
|
||
|
||
Outline* pOutlineAdd = pDocument->getOutline();
|
||
GList* pListAdd = NULL;
|
||
if (pOutlineAdd)
|
||
pListAdd = pOutlineAdd->getItems();
|
||
if (!pListAdd)
|
||
return bRes;
|
||
|
||
PdfWriter::CDocument* pDoc = m_pWriter->GetDocument();
|
||
PDFDoc* pDocumentFirst = m_pReader->GetPDFDocument(0);
|
||
Outline* pOutlineOld = pDocumentFirst->getOutline();
|
||
GList* pListOld = NULL;
|
||
if (pOutlineOld)
|
||
pListOld = pOutlineOld->getItems();
|
||
if (!pDoc->GetOutlines() && pListOld)
|
||
{
|
||
for (int i = 0, num = pListOld->getLength(); i < num; i++)
|
||
{
|
||
OutlineItem* pOutlineItem = (OutlineItem*)pListOld->get(i);
|
||
if (pOutlineItem)
|
||
CreateOutlines(pDocumentFirst, pDoc, pOutlineItem, NULL);
|
||
}
|
||
}
|
||
|
||
std::wstring wsFileName = NSFile::GetFileName(wsPath);
|
||
std::string sFileName = U_TO_UTF8(wsFileName);
|
||
PdfWriter::COutline* pOutline = pDoc->CreateOutline(NULL, sFileName.c_str());
|
||
for (int i = 0, num = pListAdd->getLength(); i < num; i++)
|
||
{
|
||
OutlineItem* pOutlineItem = (OutlineItem*)pListAdd->get(i);
|
||
if (pOutlineItem)
|
||
CreateOutlines(pDocumentFirst, pDoc, pOutlineItem, pOutline);
|
||
}
|
||
|
||
return bRes;
|
||
}
|
||
bool CPdfEditor::DeletePage(int nPageIndex)
|
||
{
|
||
if (m_nMode == Mode::Unknown && !IncrementalUpdates())
|
||
return false;
|
||
|
||
WriteRedact({});
|
||
m_arrRedact.clear();
|
||
|
||
PdfWriter::CDocument* pDoc = m_pWriter->GetDocument();
|
||
PdfWriter::CPage* pPage = pDoc->GetPage(nPageIndex);
|
||
int nObjID = m_mObjManager.FindObj(pPage);
|
||
bool bRes = pDoc->DeletePage(nPageIndex);
|
||
if (bRes && nObjID > 0)
|
||
{
|
||
m_mObjManager.RemoveObj(nObjID);
|
||
pDoc->RemoveObj(pPage);
|
||
}
|
||
return bRes;
|
||
}
|
||
bool CPdfEditor::AddPage(int nPageIndex)
|
||
{
|
||
if (m_nMode == Mode::Unknown && !IncrementalUpdates())
|
||
return false;
|
||
|
||
WriteRedact({});
|
||
m_arrRedact.clear();
|
||
|
||
if (m_nMode == Mode::Split)
|
||
{
|
||
std::vector<int>::iterator it = std::find(m_mObjManager.m_arrSplitAddPages.begin(), m_mObjManager.m_arrSplitAddPages.end(), m_nOriginIndex++);
|
||
if (it == m_mObjManager.m_arrSplitAddPages.end())
|
||
{
|
||
PdfWriter::CPageTree* pPageTree = m_pWriter->GetDocument()->GetPageTree();
|
||
pPageTree->CreateFakePages(1, nPageIndex);
|
||
return false;
|
||
}
|
||
m_mObjManager.m_arrSplitAddPages.erase(it);
|
||
}
|
||
|
||
// Mode WriteNew & WriteAppend
|
||
m_nEditPage = -1;
|
||
// Применение добавления страницы для writer
|
||
if (!m_pWriter->AddPage(nPageIndex))
|
||
return false;
|
||
// По умолчанию выставляются размеры первой страницы, в дальнейшем размеры можно изменить
|
||
double dPageDpiX, dPageDpiY;
|
||
double dWidth, dHeight;
|
||
m_pReader->GetPageInfo(0, &dWidth, &dHeight, &dPageDpiX, &dPageDpiY);
|
||
|
||
dWidth *= 25.4 / dPageDpiX;
|
||
dHeight *= 25.4 / dPageDpiY;
|
||
|
||
m_pWriter->put_Width(dWidth);
|
||
m_pWriter->put_Height(dHeight);
|
||
return true;
|
||
}
|
||
bool CPdfEditor::MovePage(int nPageIndex, int nPos)
|
||
{
|
||
WriteRedact({});
|
||
m_arrRedact.clear();
|
||
|
||
if (m_nMode == Mode::Split || m_nMode == Mode::WriteNew || EditPage(nPageIndex, true, true))
|
||
return m_pWriter->GetDocument()->MovePage(nPageIndex, nPos);
|
||
return false;
|
||
}
|
||
bool CPdfEditor::EditAnnot(int _nPageIndex, int nID)
|
||
{
|
||
PdfWriter::CObjectBase* pObj = m_mObjManager.GetObj(nID);
|
||
if (pObj)
|
||
return true;
|
||
|
||
PDFDoc* pPDFDocument = NULL;
|
||
PdfReader::CPdfFontList* pFontList = NULL;
|
||
int nStartRefID = 0;
|
||
int nPageIndex = m_pReader->GetPageIndex(_nPageIndex, &pPDFDocument, &pFontList, &nStartRefID);
|
||
PdfWriter::CDocument* pDoc = m_pWriter->GetDocument();
|
||
if (nPageIndex < 0 || !pPDFDocument || !pDoc)
|
||
return false;
|
||
|
||
if (pDoc->GetAnnot(nID))
|
||
return true;
|
||
|
||
if (m_nMode == Mode::Split || m_nMode == Mode::WriteNew)
|
||
return false;
|
||
|
||
XRef* xref = pPDFDocument->getXRef();
|
||
Ref* pPageRef = pPDFDocument->getCatalog()->getPageRef(nPageIndex);
|
||
if (!xref || !pPageRef || pPageRef->num == 0)
|
||
return false;
|
||
|
||
// Получение объекта аннотации
|
||
Object pageRefObj, pageObj, oAnnots;
|
||
pageRefObj.initRef(pPageRef->num, pPageRef->gen);
|
||
if (!pageRefObj.fetch(xref, &pageObj)->isDict() || !pageObj.dictLookup("Annots", &oAnnots)->isArray())
|
||
{
|
||
pageRefObj.free(); pageObj.free(); oAnnots.free();
|
||
return false;
|
||
}
|
||
pageRefObj.free(); pageObj.free();
|
||
|
||
Object oAnnotRef, oAnnot, oType;
|
||
for (int i = 0; i < oAnnots.arrayGetLength(); ++i)
|
||
{
|
||
if (oAnnots.arrayGetNF(i, &oAnnotRef)->isRef() && oAnnotRef.getRefNum() + nStartRefID == nID)
|
||
break;
|
||
oAnnotRef.free();
|
||
}
|
||
oAnnots.free();
|
||
if (!oAnnotRef.isRef() || !oAnnotRef.fetch(xref, &oAnnot)->isDict() || !oAnnot.dictLookup("Subtype", &oType)->isName())
|
||
{
|
||
oAnnotRef.free(); oAnnot.free(); oType.free();
|
||
return false;
|
||
}
|
||
|
||
if (!pDoc->GetEditPage(_nPageIndex))
|
||
EditPage(_nPageIndex, false);
|
||
|
||
// Воспроизведение словаря аннотации из reader для writer
|
||
PdfWriter::CXref* pXref = new PdfWriter::CXref(pDoc, oAnnotRef.getRefNum());
|
||
if (!pXref)
|
||
{
|
||
oAnnotRef.free(); oAnnot.free(); oType.free();
|
||
return false;
|
||
}
|
||
|
||
bool bIsWidget = false;
|
||
PdfWriter::CAnnotation* pAnnot = NULL;
|
||
if (oType.isName("Text"))
|
||
pAnnot = new PdfWriter::CTextAnnotation(pXref);
|
||
else if (oType.isName("Ink"))
|
||
pAnnot = new PdfWriter::CInkAnnotation(pXref);
|
||
else if (oType.isName("Line"))
|
||
pAnnot = new PdfWriter::CLineAnnotation(pXref);
|
||
else if (oType.isName("Highlight") || oType.isName("Underline") || oType.isName("Squiggly") || oType.isName("StrikeOut"))
|
||
pAnnot = new PdfWriter::CTextMarkupAnnotation(pXref);
|
||
else if (oType.isName("Square") || oType.isName("Circle"))
|
||
pAnnot = new PdfWriter::CSquareCircleAnnotation(pXref);
|
||
else if (oType.isName("Polygon") || oType.isName("PolyLine"))
|
||
pAnnot = new PdfWriter::CPolygonLineAnnotation(pXref);
|
||
else if (oType.isName("FreeText"))
|
||
{
|
||
std::map<std::wstring, std::wstring> mapFont = PdfReader::CAnnotFonts::GetAnnotFont(pPDFDocument, m_pReader->GetFontManager(), pFontList, &oAnnotRef);
|
||
m_mFonts.insert(mapFont.begin(), mapFont.end());
|
||
pAnnot = new PdfWriter::CFreeTextAnnotation(pXref);
|
||
}
|
||
else if (oType.isName("Caret"))
|
||
pAnnot = new PdfWriter::CCaretAnnotation(pXref);
|
||
else if (oType.isName("Stamp"))
|
||
pAnnot = new PdfWriter::CStampAnnotation(pXref);
|
||
else if (oType.isName("Redact"))
|
||
pAnnot = new PdfWriter::CRedactAnnotation(pXref);
|
||
else if (oType.isName("Popup"))
|
||
pAnnot = new PdfWriter::CPopupAnnotation(pXref);
|
||
else if (oType.isName("Widget"))
|
||
{
|
||
bIsWidget = true;
|
||
char* sName = NULL;
|
||
Object oFT;
|
||
if (oAnnot.dictLookup("FT", &oFT)->isName())
|
||
sName = oFT.getName();
|
||
|
||
if (!sName)
|
||
{
|
||
Object oParent, oParent2;
|
||
oAnnot.dictLookup("Parent", &oParent);
|
||
while (oParent.isDict())
|
||
{
|
||
if (oParent.dictLookup("FT", &oFT)->isName())
|
||
{
|
||
sName = oFT.getName();
|
||
break;
|
||
}
|
||
oFT.free();
|
||
oParent.dictLookup("Parent", &oParent2);
|
||
oParent.free();
|
||
oParent = oParent2;
|
||
}
|
||
oParent.free();
|
||
}
|
||
|
||
if (sName)
|
||
{
|
||
if (strcmp("Btn", sName) == 0)
|
||
{
|
||
bool bPushButton = false;
|
||
oFT.free();
|
||
int nFf = 0;
|
||
if (oAnnot.dictLookup("Ff", &oFT)->isInt())
|
||
nFf = oFT.getInt();
|
||
if (!nFf)
|
||
{
|
||
Object oParent, oParent2;
|
||
oAnnot.dictLookup("Parent", &oParent);
|
||
while (oParent.isDict())
|
||
{
|
||
if (oParent.dictLookup("Ff", &oFT)->isInt())
|
||
{
|
||
nFf = oFT.getInt();
|
||
break;
|
||
}
|
||
oFT.free();
|
||
oParent.dictLookup("Parent", &oParent2);
|
||
oParent.free();
|
||
oParent = oParent2;
|
||
}
|
||
oParent.free();
|
||
}
|
||
|
||
bPushButton = (bool)((nFf >> 16) & 1);
|
||
bool bRadiobutton = (bool)((nFf >> 15) & 1);
|
||
if (bPushButton)
|
||
pAnnot = new PdfWriter::CPushButtonWidget(pXref);
|
||
else
|
||
pAnnot = new PdfWriter::CCheckBoxWidget(pXref, bRadiobutton ? PdfWriter::WidgetRadiobutton : PdfWriter::WidgetCheckbox);
|
||
}
|
||
else if (strcmp("Tx", sName) == 0)
|
||
pAnnot = new PdfWriter::CTextWidget(pXref);
|
||
else if (strcmp("Ch", sName) == 0)
|
||
pAnnot = new PdfWriter::CChoiceWidget(pXref);
|
||
else if (strcmp("Sig", sName) == 0)
|
||
pAnnot = new PdfWriter::CSignatureWidget(pXref);
|
||
else
|
||
pAnnot = new PdfWriter::CWidgetAnnotation(pXref, PdfWriter::EAnnotType::AnnotWidget);
|
||
}
|
||
oFT.free();
|
||
}
|
||
|
||
if (!pAnnot)
|
||
{
|
||
oAnnotRef.free(); oAnnot.free(); oType.free();
|
||
RELEASEOBJECT(pXref);
|
||
return false;
|
||
}
|
||
pXref->Add(pAnnot, oAnnotRef.getRefGen());
|
||
|
||
for (int nIndex = 0; nIndex < oAnnot.dictGetLength(); ++nIndex)
|
||
{
|
||
char* chKey = oAnnot.dictGetKey(nIndex);
|
||
if (!strcmp("Popup", chKey))
|
||
{
|
||
Object oPopupRef;
|
||
if (oAnnot.dictGetValNF(nIndex, &oPopupRef)->isRef() && EditAnnot(nPageIndex, oPopupRef.getRefNum() + nStartRefID))
|
||
{
|
||
PdfWriter::CAnnotation* pPopup = pDoc->GetAnnot(oPopupRef.getRefNum() + nStartRefID);
|
||
if (pPopup)
|
||
{
|
||
pAnnot->Add("Popup", pPopup);
|
||
pPopup->Add("Parent", pAnnot);
|
||
}
|
||
}
|
||
continue;
|
||
}
|
||
else if (!strcmp("Parent", chKey) && bIsWidget)
|
||
{
|
||
Object oParentRef;
|
||
oAnnot.dictGetValNF(nIndex, &oParentRef);
|
||
PdfWriter::CDictObject* pParent = GetWidgetParent(pPDFDocument, pDoc, &oParentRef, nStartRefID);
|
||
|
||
if (!pParent)
|
||
{
|
||
oParentRef.free();
|
||
continue;
|
||
}
|
||
|
||
((PdfWriter::CWidgetAnnotation*)pAnnot)->SetParent(pParent);
|
||
PdfWriter::CArrayObject* pKids = dynamic_cast<PdfWriter::CArrayObject*>(pParent->Get("Kids"));
|
||
if (!pKids)
|
||
{
|
||
oParentRef.free();
|
||
continue;
|
||
}
|
||
|
||
for (int i = 0; i < pKids->GetCount(); ++i)
|
||
{
|
||
PdfWriter::CObjectBase* pKid = pKids->Get(i);
|
||
if (pKid->GetObjId() == oAnnotRef.getRefNum())
|
||
{
|
||
pKids->Insert(pKid, pAnnot, true);
|
||
break;
|
||
}
|
||
}
|
||
oParentRef.free();
|
||
continue;
|
||
}
|
||
else if (!strcmp("AP", chKey) && pAnnot->GetAnnotationType() == PdfWriter::AnnotWidget)
|
||
{
|
||
PdfWriter::EWidgetType nType = ((PdfWriter::CWidgetAnnotation*)pAnnot)->GetWidgetType();
|
||
if (nType == PdfWriter::WidgetRadiobutton || nType == PdfWriter::WidgetCheckbox)
|
||
{
|
||
PdfWriter::CCheckBoxWidget* pCAnnot = dynamic_cast<PdfWriter::CCheckBoxWidget*>(pAnnot);
|
||
|
||
Object oAP, oN;
|
||
if (oAnnot.dictGetVal(nIndex, &oAP)->isDict() && oAP.dictLookup("N", &oN)->isDict())
|
||
{
|
||
for (int j = 0, nNormLength = oN.dictGetLength(); j < nNormLength; ++j)
|
||
{
|
||
std::string sNormName(oN.dictGetKey(j));
|
||
if (sNormName != "Off")
|
||
{
|
||
pCAnnot->SetAP_N_Yes(UTF8_TO_U(sNormName));
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
Object oTemp;
|
||
oAnnot.dictGetValNF(nIndex, &oTemp);
|
||
DictToCDictObject(&oTemp, pAnnot, chKey);
|
||
oTemp.free();
|
||
}
|
||
|
||
if (oType.isName("Stamp"))
|
||
{
|
||
Object oAP, oAPN;
|
||
if (oAnnot.dictLookup("AP", &oAP)->isDict() && oAP.dictLookup("N", &oAPN)->isStream())
|
||
{
|
||
Object oAPNRef;
|
||
oAP.dictLookupNF("N", &oAPNRef);
|
||
PdfWriter::CXref* pXRef = new PdfWriter::CXref(pDoc, oAPNRef.getRefNum());
|
||
pDoc->EditXref(pXRef);
|
||
|
||
PdfWriter::CDictObject* pAPN = new PdfWriter::CDictObject();
|
||
pXRef->Add(pAPN, oAPNRef.getRefGen());
|
||
((PdfWriter::CStampAnnotation*)pAnnot)->SetAPStream(pAPN);
|
||
oAPNRef.free();
|
||
|
||
Object oTemp;
|
||
Dict* pODict = oAPN.streamGetDict();
|
||
for (int nIndex = 0; nIndex < pODict->getLength(); ++nIndex)
|
||
{
|
||
char* chKey = pODict->getKey(nIndex);
|
||
pODict->getValNF(nIndex, &oTemp);
|
||
DictToCDictObject(&oTemp, pAPN, chKey);
|
||
oTemp.free();
|
||
}
|
||
int nLength = 0;
|
||
if (pODict->lookup("Length", &oTemp)->isInt())
|
||
nLength = oTemp.getInt();
|
||
PdfWriter::CStream* pStream = new PdfWriter::CMemoryStream(nLength);
|
||
pAPN->SetStream(pStream);
|
||
pAPN->Add("Length", nLength);
|
||
Stream* pOStream = oAPN.getStream()->getUndecodedStream();
|
||
pOStream->reset();
|
||
for (int nI = 0; nI < nLength; ++nI)
|
||
pStream->WriteChar(pOStream->getChar());
|
||
}
|
||
oAP.free(); oAPN.free();
|
||
}
|
||
oAnnotRef.free(); oAnnot.free(); oType.free();
|
||
|
||
if (pDoc->EditAnnot(pXref, pAnnot, nID))
|
||
return true;
|
||
|
||
RELEASEOBJECT(pXref);
|
||
return false;
|
||
}
|
||
bool CPdfEditor::DeleteAnnot(int nID, Object* oAnnots)
|
||
{
|
||
PdfWriter::CDocument* pDoc = m_pWriter->GetDocument();
|
||
if (!pDoc)
|
||
return false;
|
||
|
||
PdfWriter::CObjectBase* pObj = m_mObjManager.GetObj(nID);
|
||
if (pObj)
|
||
{
|
||
m_mObjManager.RemoveObj(nID);
|
||
pDoc->RemoveObj(pObj);
|
||
return true;
|
||
}
|
||
if (m_nMode == Mode::Split || m_nMode == Mode::WriteNew)
|
||
return true;
|
||
|
||
PDFDoc* pPDFDocument = NULL;
|
||
int nPageIndex = m_pReader->GetPageIndex(m_nEditPage, &pPDFDocument);
|
||
if (nPageIndex < 0 || !pPDFDocument)
|
||
return false;
|
||
|
||
XRef* xref = pPDFDocument->getXRef();
|
||
bool bClear = false;
|
||
if (!oAnnots)
|
||
{
|
||
PdfWriter::CPage* pPage = pDoc->GetCurPage();
|
||
std::pair<int, int> pPageRef = { pPage->GetObjId(), pPage->GetGenNo() };
|
||
if (pPageRef.first == 0)
|
||
return false;
|
||
|
||
oAnnots = new Object();
|
||
bClear = true;
|
||
|
||
// Получение объекта аннотации
|
||
Object pageRefObj, pageObj;
|
||
pageRefObj.initRef(pPageRef.first, pPageRef.second);
|
||
if (!pageRefObj.fetch(xref, &pageObj)->isDict() || !pageObj.dictLookup("Annots", oAnnots)->isArray())
|
||
{
|
||
pageRefObj.free(); pageObj.free(); oAnnots->free();
|
||
RELEASEOBJECT(oAnnots);
|
||
return false;
|
||
}
|
||
pageRefObj.free(); pageObj.free();
|
||
}
|
||
|
||
bool bRes = false;
|
||
for (int i = 0; i < oAnnots->arrayGetLength(); ++i)
|
||
{
|
||
Object oAnnotRef, oAnnot;
|
||
if (oAnnots->arrayGetNF(i, &oAnnotRef)->isRef() && oAnnotRef.getRefNum() == nID)
|
||
{
|
||
bool bNeed = false;
|
||
if (oAnnotRef.fetch(xref, &oAnnot)->isDict())
|
||
{
|
||
Object oType;
|
||
if (oAnnot.dictLookup("Subtype", &oType)->isName("Widget"))
|
||
{
|
||
char* sName = NULL;
|
||
Object oFT;
|
||
if (oAnnot.dictLookup("FT", &oFT)->isName())
|
||
sName = oFT.getName();
|
||
|
||
if (!sName)
|
||
{
|
||
Object oParent, oParent2;
|
||
oAnnot.dictLookup("Parent", &oParent);
|
||
while (oParent.isDict())
|
||
{
|
||
if (oParent.dictLookup("FT", &oFT)->isName())
|
||
{
|
||
sName = oFT.getName();
|
||
break;
|
||
}
|
||
oFT.free();
|
||
oParent.dictLookup("Parent", &oParent2);
|
||
oParent.free();
|
||
oParent = oParent2;
|
||
}
|
||
oParent.free();
|
||
}
|
||
|
||
if (sName && strcmp("Btn", sName) == 0)
|
||
{
|
||
bool bPushButton = false;
|
||
oFT.free();
|
||
int nFf = 0;
|
||
if (oAnnot.dictLookup("Ff", &oFT)->isInt())
|
||
nFf = oFT.getInt();
|
||
if (!nFf)
|
||
{
|
||
Object oParent, oParent2;
|
||
oAnnot.dictLookup("Parent", &oParent);
|
||
while (oParent.isDict())
|
||
{
|
||
if (oParent.dictLookup("Ff", &oFT)->isInt())
|
||
{
|
||
nFf = oFT.getInt();
|
||
break;
|
||
}
|
||
oFT.free();
|
||
oParent.dictLookup("Parent", &oParent2);
|
||
oParent.free();
|
||
oParent = oParent2;
|
||
}
|
||
oParent.free();
|
||
}
|
||
|
||
bPushButton = (bool)((nFf >> 16) & 1);
|
||
bool bRadiosInUnison = (bool)(nFf & (1 << 25));
|
||
if (!bPushButton)
|
||
{
|
||
oFT.free();
|
||
bNeed = oAnnot.dictLookup("Opt", &oFT)->isArray() == gTrue;
|
||
if (!bNeed)
|
||
{
|
||
Object oParent, oParent2;
|
||
oAnnot.dictLookup("Parent", &oParent);
|
||
while (oParent.isDict())
|
||
{
|
||
if (oParent.dictLookup("Opt", &oFT)->isArray())
|
||
{
|
||
bNeed = true;
|
||
break;
|
||
}
|
||
oFT.free();
|
||
oParent.dictLookup("Parent", &oParent2);
|
||
oParent.free();
|
||
oParent = oParent2;
|
||
}
|
||
oParent.free();
|
||
}
|
||
if (bNeed && EditAnnot(m_nEditPage, nID))
|
||
{
|
||
PdfWriter::CAnnotation* pAnnot = pDoc->GetAnnot(nID);
|
||
if (pAnnot)
|
||
{
|
||
pAnnot->SetHidden();
|
||
|
||
PdfWriter::CObjectBase* pObj = pAnnot->Get("Parent");
|
||
PdfWriter::CDictObject* pParent = NULL;
|
||
if (pObj && pObj->GetType() == PdfWriter::object_type_DICT)
|
||
pParent = (PdfWriter::CDictObject*)pObj;
|
||
PdfWriter::CArrayObject* pOpt = NULL, *pKids = NULL;
|
||
if (pParent)
|
||
{
|
||
pObj = pParent->Get("Kids");
|
||
if (pObj && pObj->GetType() == PdfWriter::object_type_ARRAY)
|
||
pKids = (PdfWriter::CArrayObject*)pObj;
|
||
pObj = pParent->Get("Opt");
|
||
if (pObj && pObj->GetType() == PdfWriter::object_type_ARRAY)
|
||
pOpt = (PdfWriter::CArrayObject*)pObj;
|
||
}
|
||
std::map<std::wstring, std::wstring> mNameAP_N_Yes;
|
||
if (pKids && pOpt && pKids->GetCount() == pOpt->GetCount())
|
||
{
|
||
for (int i = 0; i < pKids->GetCount(); ++i)
|
||
{
|
||
pObj = pKids->Get(i);
|
||
if (pObj == pAnnot)
|
||
{
|
||
pObj = pKids->Remove(i);
|
||
delete pObj;
|
||
pObj = pOpt->Remove(i);
|
||
delete pObj;
|
||
--i;
|
||
}
|
||
else
|
||
{
|
||
pObj = pOpt->Get(i);
|
||
if (pObj->GetType() == PdfWriter::object_type_ARRAY && ((PdfWriter::CArrayObject*)pObj)->GetCount() > 0)
|
||
pObj = ((PdfWriter::CArrayObject*)pObj)->Get(0);
|
||
std::wstring sNameOpt;
|
||
if (pObj->GetType() == PdfWriter::object_type_STRING)
|
||
{
|
||
PdfWriter::CStringObject* pStr = (PdfWriter::CStringObject*)pObj;
|
||
sNameOpt = NSFile::CUtf8Converter::GetUnicodeStringFromUTF8((BYTE*)pStr->GetString(), pStr->GetLength());
|
||
if (mNameAP_N_Yes.find(sNameOpt) == mNameAP_N_Yes.end())
|
||
mNameAP_N_Yes[sNameOpt] = std::to_wstring(i);
|
||
}
|
||
pObj = pKids->Get(i);
|
||
Object oAnnot, oSubtype, oPageRef;
|
||
if (xref->fetch(pObj->GetObjId(), pObj->GetGenNo(), &oAnnot)->isDict("Annot") && oAnnot.dictLookup("Subtype", &oSubtype)->isName("Widget") &&
|
||
oAnnot.dictLookupNF("P", &oPageRef)->isRef())
|
||
{
|
||
int nPage = pPDFDocument->findPage(oPageRef.getRefNum(), oPageRef.getRefGen()) - 1;
|
||
PdfWriter::CCheckBoxWidget* pKidAnnot = NULL;
|
||
int nObjId = pObj->GetObjId();
|
||
if (nPage >= 0 && EditAnnot(nPage, nObjId))
|
||
pKidAnnot = dynamic_cast<PdfWriter::CCheckBoxWidget*>(pDoc->GetAnnot(nObjId));
|
||
if (pKidAnnot && !sNameOpt.empty())
|
||
pKidAnnot->RenameAP_N_Yes((bRadiosInUnison || pKidAnnot->GetWidgetType() == PdfWriter::WidgetCheckbox) ? mNameAP_N_Yes[sNameOpt] : std::to_wstring(i));
|
||
}
|
||
oAnnot.free(); oSubtype.free(); oPageRef.free();
|
||
}
|
||
}
|
||
}
|
||
|
||
Object oPopupRef;
|
||
if (oAnnot.dictLookupNF("Popup", &oPopupRef)->isRef())
|
||
{
|
||
pAnnot = pDoc->GetAnnot(oPopupRef.getRefNum());
|
||
if (pAnnot)
|
||
pAnnot->SetHidden();
|
||
}
|
||
oPopupRef.free();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
oFT.free();
|
||
}
|
||
oType.free();
|
||
|
||
if (!bNeed)
|
||
{
|
||
Object oPopupRef;
|
||
if (oAnnot.dictLookupNF("Popup", &oPopupRef)->isRef())
|
||
pDoc->DeleteAnnot(oPopupRef.getRefNum(), oPopupRef.getRefGen());
|
||
oPopupRef.free();
|
||
}
|
||
}
|
||
if (!bNeed)
|
||
bRes = pDoc->DeleteAnnot(oAnnotRef.getRefNum(), oAnnotRef.getRefGen());
|
||
}
|
||
else if (oAnnots->arrayGet(i, &oAnnot)->isDict())
|
||
{
|
||
Object oIRTRef;
|
||
if (oAnnot.dictLookupNF("IRT", &oIRTRef)->isRef() && oIRTRef.getRefNum() == nID)
|
||
DeleteAnnot(oAnnotRef.getRefNum(), oAnnots);
|
||
oIRTRef.free();
|
||
}
|
||
oAnnotRef.free(); oAnnot.free();
|
||
}
|
||
|
||
if (bClear)
|
||
{
|
||
oAnnots->free();
|
||
RELEASEOBJECT(oAnnots);
|
||
}
|
||
|
||
return bRes;
|
||
}
|
||
bool CPdfEditor::EditWidgets(IAdvancedCommand* pCommand)
|
||
{
|
||
if (m_nMode == Mode::Unknown && !IncrementalUpdates())
|
||
return false;
|
||
|
||
WriteRedact({});
|
||
m_arrRedact.clear();
|
||
|
||
CWidgetsInfo* pFieldInfo = (CWidgetsInfo*)pCommand;
|
||
PdfWriter::CDocument* pDoc = m_pWriter->GetDocument();
|
||
|
||
const std::vector< std::pair<int, int> >& arrCO = pFieldInfo->GetCO();
|
||
for (int i = 0; i < arrCO.size(); ++i)
|
||
{
|
||
int nObjNum = arrCO[i].first;
|
||
if (pDoc->GetParent(nObjNum))
|
||
continue;
|
||
if (pDoc->GetAnnot(nObjNum))
|
||
continue;
|
||
|
||
PDFDoc* pPDFDocument = NULL;
|
||
int nStartRefID = 0;
|
||
int nRefID = m_pReader->FindRefNum(nObjNum, &pPDFDocument, &nStartRefID);
|
||
if (nRefID < 0)
|
||
continue;
|
||
|
||
XRefEntry* pEntry = pPDFDocument->getXRef()->getEntry(nRefID);
|
||
pFieldInfo->ChangeCO(i, nRefID, pEntry->type == xrefEntryCompressed ? 0 : pEntry->gen);
|
||
}
|
||
|
||
const std::vector<CWidgetsInfo::CParent*>& arrParents = pFieldInfo->GetParents();
|
||
for (CWidgetsInfo::CParent* pParent : arrParents)
|
||
{
|
||
PdfWriter::CDictObject* pDParent = pDoc->GetParent(pParent->nID);
|
||
if (pDParent)
|
||
continue;
|
||
|
||
PDFDoc* pPDFDocument = NULL;
|
||
int nStartRefID = 0;
|
||
int nRefID = m_pReader->FindRefNum(pParent->nID, &pPDFDocument, &nStartRefID);
|
||
if (nRefID < 0)
|
||
continue;
|
||
|
||
XRefEntry* pEntry = pPDFDocument->getXRef()->getEntry(nRefID);
|
||
Object oParentRef;
|
||
oParentRef.initRef(nRefID, pEntry->type == xrefEntryCompressed ? 0 : pEntry->gen);
|
||
GetWidgetParent(pPDFDocument, pDoc, &oParentRef, nStartRefID);
|
||
// TODO перевыставить детей
|
||
oParentRef.free();
|
||
}
|
||
return true;
|
||
}
|
||
int CPdfEditor::GetPagesCount()
|
||
{
|
||
return m_pWriter->GetDocument()->GetPagesCount();
|
||
}
|
||
void CPdfEditor::GetPageInfo(int nPageIndex, double* pdWidth, double* pdHeight, double* pdDpiX, double* pdDpiY)
|
||
{
|
||
PdfWriter::CPage* pPage = m_pWriter->GetDocument()->GetPage(nPageIndex);
|
||
if (!pPage)
|
||
return;
|
||
|
||
int nRotate = pPage->GetRotate();
|
||
if (nRotate % 180 == 0)
|
||
{
|
||
*pdWidth = pPage->GetWidth();
|
||
*pdHeight = pPage->GetHeight();
|
||
}
|
||
else
|
||
{
|
||
*pdWidth = pPage->GetHeight();
|
||
*pdHeight = pPage->GetWidth();
|
||
}
|
||
|
||
*pdDpiX = 72.0;
|
||
*pdDpiY = 72.0;
|
||
}
|
||
int CPdfEditor::GetRotate(int nPageIndex)
|
||
{
|
||
PdfWriter::CPage* pPage = m_pWriter->GetDocument()->GetPage(nPageIndex);
|
||
if (!pPage)
|
||
return 0;
|
||
return pPage->GetRotate();
|
||
}
|
||
bool CPdfEditor::IsEditPage()
|
||
{
|
||
return m_nEditPage >= 0;
|
||
}
|
||
void CPdfEditor::ClearPage()
|
||
{
|
||
PDFDoc* pPDFDocument = NULL;
|
||
int nPageIndex = m_pReader->GetPageIndex(m_nEditPage, &pPDFDocument);
|
||
PdfWriter::CDocument* pDoc = m_pWriter->GetDocument();
|
||
if (nPageIndex < 0 || !pPDFDocument || !pDoc)
|
||
return;
|
||
XRef* xref = pPDFDocument->getXRef();
|
||
Ref* pPageRef = pPDFDocument->getCatalog()->getPageRef(nPageIndex);
|
||
|
||
// Получение объекта страницы
|
||
Object pageRefObj, pageObj;
|
||
pageRefObj.initRef(pPageRef->num, pPageRef->gen);
|
||
if (!pageRefObj.fetch(xref, &pageObj)->isDict())
|
||
{
|
||
pageObj.free(); pageRefObj.free();
|
||
return;
|
||
}
|
||
pageRefObj.free();
|
||
|
||
Object oAnnots;
|
||
// ВРЕМЕННО удаление Link аннотаций при редактировании
|
||
if (pageObj.dictLookup("Annots", &oAnnots)->isArray())
|
||
{
|
||
for (int nIndex = 0; nIndex < oAnnots.arrayGetLength(); ++nIndex)
|
||
{
|
||
Object oAnnot, oSubtype, oAnnotRef;
|
||
if (oAnnots.arrayGet(nIndex, &oAnnot)->isDict("Annot") && oAnnot.dictLookup("Subtype", &oSubtype)->isName("Link"))
|
||
{
|
||
oAnnots.arrayGetNF(nIndex, &oAnnotRef);
|
||
DeleteAnnot(oAnnotRef.getRefNum(), &oAnnots);
|
||
}
|
||
oAnnot.free(); oSubtype.free(); oAnnotRef.free();
|
||
}
|
||
}
|
||
pageObj.free();
|
||
|
||
if (m_nMode == Mode::Split || m_nMode == Mode::WriteNew)
|
||
pDoc->ClearPageFull();
|
||
else
|
||
pDoc->ClearPage();
|
||
|
||
Page* pOPage = pPDFDocument->getCatalog()->getPage(nPageIndex);
|
||
if (pOPage->isCropped())
|
||
{
|
||
PDFRectangle* pCropBox = pOPage->getCropBox();
|
||
PdfWriter::CStream* pStream = pDoc->GetCurPage()->GetStream();
|
||
pStream->WriteStr("1 0 0 1 ");
|
||
pStream->WriteReal(pCropBox->x1);
|
||
pStream->WriteChar(' ');
|
||
pStream->WriteReal(pCropBox->y2 - pOPage->getMediaBox()->y2);
|
||
pStream->WriteStr(" cm\012");
|
||
}
|
||
}
|
||
void CPdfEditor::AddShapeXML(const std::string& sXML)
|
||
{
|
||
return m_pWriter->GetDocument()->AddShapeXML(sXML);
|
||
}
|
||
void CPdfEditor::EndMarkedContent()
|
||
{
|
||
m_pWriter->GetDocument()->EndShapeXML();
|
||
}
|
||
bool CPdfEditor::IsBase14(const std::wstring& wsFontName, bool& bBold, bool& bItalic, std::wstring& wsFontPath)
|
||
{
|
||
std::map<std::wstring, std::wstring>::const_iterator it = m_mFonts.find(wsFontName);
|
||
if (it != m_mFonts.end())
|
||
wsFontPath = it->second;
|
||
if (wsFontPath.empty())
|
||
{
|
||
const std::map<std::wstring, std::wstring>& mFonts = m_pReader->GetFonts();
|
||
std::map<std::wstring, std::wstring>::const_iterator it2 = mFonts.find(wsFontName);
|
||
if (it2 != mFonts.end())
|
||
wsFontPath = it2->second;
|
||
}
|
||
if (wsFontPath.empty())
|
||
return false;
|
||
if (wsFontName == L"Helvetica")
|
||
return true;
|
||
if (wsFontName == L"Helvetica-Bold")
|
||
{
|
||
bBold = true;
|
||
return true;
|
||
}
|
||
if (wsFontName == L"Helvetica-Oblique")
|
||
{
|
||
bItalic = true;
|
||
return true;
|
||
}
|
||
if (wsFontName == L"Helvetice-BoldOblique")
|
||
{
|
||
bBold = true;
|
||
bItalic = true;
|
||
return true;
|
||
}
|
||
if (wsFontName == L"Courier")
|
||
return true;
|
||
if (wsFontName == L"Courier-Bold")
|
||
{
|
||
bBold = true;
|
||
return true;
|
||
}
|
||
if (wsFontName == L"Courier-Oblique")
|
||
{
|
||
bItalic = true;
|
||
return true;
|
||
}
|
||
if (wsFontName == L"Courier-BoldOblique")
|
||
{
|
||
bBold = true;
|
||
bItalic = true;
|
||
return true;
|
||
}
|
||
if (wsFontName == L"Times" || wsFontName == L"Times-Roman")
|
||
return true;
|
||
if (wsFontName == L"Times-Bold")
|
||
{
|
||
bBold = true;
|
||
return true;
|
||
}
|
||
if (wsFontName == L"Times-Oblique")
|
||
{
|
||
bItalic = true;
|
||
return true;
|
||
}
|
||
if (wsFontName == L"Times-BoldOblique")
|
||
{
|
||
bBold = true;
|
||
bItalic = true;
|
||
return true;
|
||
}
|
||
if (wsFontName == L"Symbol")
|
||
return true;
|
||
if (wsFontName == L"ZapfDingbats")
|
||
return true;
|
||
return false;
|
||
}
|
||
void CPdfEditor::Redact(IAdvancedCommand* _pCommand)
|
||
{
|
||
PDFDoc* pPDFDocument = NULL;
|
||
PDFRectangle* cropBox = NULL;
|
||
int nPageIndex = -1;
|
||
Page* pPage = NULL;
|
||
bool bEditPage = IsEditPage();
|
||
PdfWriter::CDocument* pDoc = m_pWriter->GetDocument();
|
||
if (bEditPage)
|
||
{
|
||
nPageIndex = m_pReader->GetPageIndex(m_nEditPage, &pPDFDocument);
|
||
if (nPageIndex < 0 || !pPDFDocument)
|
||
return;
|
||
pPage = pPDFDocument->getCatalog()->getPage(nPageIndex);
|
||
cropBox = pPage->getCropBox();
|
||
}
|
||
else
|
||
{
|
||
cropBox = new PDFRectangle();
|
||
PdfWriter::CPage* pWPage = pDoc->GetCurPage();
|
||
cropBox->x2 = pWPage->GetWidth();
|
||
cropBox->y2 = pWPage->GetHeight();
|
||
}
|
||
|
||
std::vector<double> arrAllQuads;
|
||
CRedact* pCommand = (CRedact*)_pCommand;
|
||
std::vector<CRedact::SRedact*> arrRedacts = pCommand->GetRedact();
|
||
for (CRedact::SRedact* pRedact : arrRedacts)
|
||
{
|
||
m_arrRedact.push_back(CRedactData());
|
||
m_arrRedact.back().sID = pRedact->sID;
|
||
m_arrRedact.back().arrQuads = pRedact->arrQuadPoints;
|
||
for (int i = 0; i < pRedact->arrQuadPoints.size(); i += 2)
|
||
{
|
||
arrAllQuads.push_back(pRedact->arrQuadPoints[i + 0] + cropBox->x1);
|
||
arrAllQuads.push_back(cropBox->y2 - pRedact->arrQuadPoints[i + 1]);
|
||
}
|
||
int nFlags = pRedact->nFlag;
|
||
if (nFlags & (1 << 0))
|
||
{
|
||
m_arrRedact.back().pRender = pRedact->pRender;
|
||
m_arrRedact.back().nLenRender = pRedact->nRenderLen;
|
||
}
|
||
}
|
||
|
||
if (bEditPage)
|
||
{
|
||
PdfWriter::RedactOutputDev oRedactOut(m_pWriter);
|
||
oRedactOut.NewPDF(pPDFDocument->getXRef());
|
||
oRedactOut.SetRedact(arrAllQuads);
|
||
|
||
Object oContents;
|
||
pPage->getContents(&oContents);
|
||
PDFRectangle* box = pPage->getMediaBox();
|
||
Gfx* gfx = new Gfx(pPDFDocument, &oRedactOut, m_nEditPage, pPage->getResourceDict(), 72.0, 72.0, box, NULL, 0);
|
||
gfx->saveState();
|
||
gfx->display(&oContents);
|
||
gfx->endOfPage();
|
||
oContents.free();
|
||
RELEASEOBJECT(gfx);
|
||
|
||
PdfWriter::CPage* pWPage = pDoc->GetCurPage();
|
||
pWPage->SetFontType(PdfWriter::EFontType::fontUnknownType);
|
||
}
|
||
else
|
||
{
|
||
RELEASEOBJECT(cropBox);
|
||
}
|
||
}
|
||
std::vector<double> CPdfEditor::WriteRedact(const std::vector<std::wstring>& arrID)
|
||
{
|
||
std::wstring sID;
|
||
if (!arrID.empty())
|
||
sID = arrID[0];
|
||
std::vector<double> arrRes;
|
||
for (int i = 0; i < m_arrRedact.size(); ++i)
|
||
{
|
||
if (m_arrRedact[i].sID == sID)
|
||
{
|
||
for (int j = i; j < m_arrRedact.size(); ++j)
|
||
arrRes.insert(arrRes.end(), m_arrRedact[j].arrQuads.begin(), m_arrRedact[j].arrQuads.end());
|
||
return arrRes;
|
||
}
|
||
|
||
CRedactData oRedact = m_arrRedact[i];
|
||
if (oRedact.bDraw || !oRedact.pRender || oRedact.nLenRender != oRedact.arrQuads.size() / 8 * 12)
|
||
continue;
|
||
|
||
BYTE* pMemory = oRedact.pRender;
|
||
m_pWriter->AddRedact({});
|
||
double dM1, dM2, dM3, dM4, dM5, dM6;
|
||
m_pWriter->GetTransform(&dM1, &dM2, &dM3, &dM4, &dM5, &dM6);
|
||
LONG lType, lColorB, lAlpha1, lAlpha2;
|
||
m_pWriter->get_BrushType(&lType);
|
||
m_pWriter->get_BrushColor1(&lColorB);
|
||
m_pWriter->get_BrushAlpha1(&lAlpha1);
|
||
m_pWriter->get_BrushAlpha2(&lAlpha2);
|
||
|
||
m_pWriter->SetTransform(1, 0, 0, 1, 0, 0);
|
||
m_pWriter->PathCommandEnd();
|
||
m_pWriter->put_BrushType(c_BrushTypeSolid);
|
||
m_pWriter->put_BrushAlpha1(255);
|
||
m_pWriter->put_BrushAlpha2(255);
|
||
|
||
for (int i = 0; i < oRedact.arrQuads.size(); i += 8)
|
||
{
|
||
int ret = *((int*)pMemory);
|
||
pMemory += 4;
|
||
LONG R = ret;
|
||
ret = *((int*)pMemory);
|
||
pMemory += 4;
|
||
LONG G = ret;
|
||
ret = *((int*)pMemory);
|
||
pMemory += 4;
|
||
LONG B = ret;
|
||
LONG lColor = (LONG)(R | (G << 8) | (B << 16) | ((LONG)255 << 24));
|
||
|
||
m_pWriter->put_BrushColor1(lColor);
|
||
m_pWriter->PathCommandMoveTo(PdfReader::PDFCoordsToMM(oRedact.arrQuads[i + 0]), PdfReader::PDFCoordsToMM(oRedact.arrQuads[i + 1]));
|
||
m_pWriter->PathCommandLineTo(PdfReader::PDFCoordsToMM(oRedact.arrQuads[i + 2]), PdfReader::PDFCoordsToMM(oRedact.arrQuads[i + 3]));
|
||
m_pWriter->PathCommandLineTo(PdfReader::PDFCoordsToMM(oRedact.arrQuads[i + 4]), PdfReader::PDFCoordsToMM(oRedact.arrQuads[i + 5]));
|
||
m_pWriter->PathCommandLineTo(PdfReader::PDFCoordsToMM(oRedact.arrQuads[i + 6]), PdfReader::PDFCoordsToMM(oRedact.arrQuads[i + 7]));
|
||
m_pWriter->PathCommandClose();
|
||
m_pWriter->DrawPath(NULL, L"", c_nWindingFillMode);
|
||
m_pWriter->PathCommandEnd();
|
||
}
|
||
|
||
m_pWriter->SetTransform(dM1, dM2, dM3, dM4, dM5, dM6);
|
||
m_pWriter->put_BrushType(lType);
|
||
m_pWriter->put_BrushColor1(lColorB);
|
||
m_pWriter->put_BrushAlpha1(lAlpha1);
|
||
m_pWriter->put_BrushAlpha2(lAlpha2);
|
||
|
||
// TODO рендер редакта должен быть пересечён со всеми последующими редактами
|
||
// TODO на самом деле должен быть рендер команд редакта
|
||
/*
|
||
PdfWriter::CPage* pCurPage = m_pWriter->GetPage();
|
||
pDoc->FixEditPage(pCurPage);
|
||
PdfWriter::CPage* pFakePage = new PdfWriter::CPage(pDoc);
|
||
m_pWriter->SetPage(pFakePage);
|
||
pDoc->SetCurPage(pFakePage);
|
||
|
||
// TODO Нужно нивелировать текущую матрицу до единичной, а потом сместить ещё на CropBox
|
||
|
||
pFakePage->SetStream(pCurPage->GetStream());
|
||
pFakePage->Add("Resources", pCurPage->Get("Resources"));
|
||
|
||
IMetafileToRenderter* pCorrector = new IMetafileToRenderter(m_pWriter->GetRenderer());
|
||
NSOnlineOfficeBinToPdf::ConvertBufferToRenderer(pRender, nLenRender, pCorrector);
|
||
RELEASEOBJECT(pCorrector);
|
||
|
||
m_pWriter->SetPage(pCurPage);
|
||
pDoc->SetCurPage(pCurPage);
|
||
RELEASEOBJECT(pFakePage);
|
||
*/
|
||
|
||
m_arrRedact[i].bDraw = true;
|
||
}
|
||
return arrRes;
|
||
}
|