Create MergePages

This commit is contained in:
Svetlana Kulikova
2025-02-18 16:00:57 +03:00
parent 229b263a9f
commit 9f3ab7cc29
7 changed files with 204 additions and 156 deletions

View File

@ -650,6 +650,7 @@ void GetCTM(XRef* pXref, Object* oPage, double* dCTM)
CPdfEditor::CPdfEditor(const std::wstring& _wsSrcFile, const std::wstring& _wsPassword, CPdfReader* _pReader, const std::wstring& _wsDstFile, CPdfWriter* _pWriter)
{
m_wsSrcFile = _wsSrcFile;
m_wsDstFile = _wsDstFile;
m_wsPassword = _wsPassword;
m_pReader = _pReader;
m_pWriter = _pWriter;
@ -665,7 +666,7 @@ CPdfEditor::CPdfEditor(const std::wstring& _wsSrcFile, const std::wstring& _wsPa
}
// Если результат редактирования будет сохранен в тот же файл, что открыт для чтения, то файл необходимо сделать редактируемым
std::string sPathUtf8New = U_TO_UTF8(_wsDstFile);
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))
{
@ -680,15 +681,15 @@ CPdfEditor::CPdfEditor(const std::wstring& _wsSrcFile, const std::wstring& _wsPa
return;
}
}
else
else if (!m_wsDstFile.empty())
{
if (!NSFile::CFileBinary::Copy(m_wsSrcFile, _wsDstFile))
if (!NSFile::CFileBinary::Copy(m_wsSrcFile, m_wsDstFile))
{
m_nError = 2;
return;
}
NSFile::CFileBinary oFile;
if (!oFile.OpenFile(_wsDstFile, true))
if (!oFile.OpenFile(m_wsDstFile, true))
{
m_nError = 2;
return;
@ -890,7 +891,7 @@ CPdfEditor::CPdfEditor(const std::wstring& _wsSrcFile, const std::wstring& _wsPa
}
// Применение редактирования для writer
bool bRes = pDoc->EditPdf(_wsDstFile, xref->getLastXRefPos(), xref->getNumObjects() + 1, pXref, pCatalog, pEncryptDict, nFormField);
bool bRes = pDoc->EditPdf(xref->getLastXRefPos(), xref->getNumObjects() + 1, pXref, pCatalog, pEncryptDict, nFormField);
if (bRes)
{
// Воспроизведение дерева страниц во writer
@ -903,7 +904,7 @@ CPdfEditor::CPdfEditor(const std::wstring& _wsSrcFile, const std::wstring& _wsPa
if (!bRes)
m_nError = 5; // Ошибка применения редактирования
}
CPdfEditor::CPdfEditor(CPdfReader* _pReader, CPdfWriter* _pWriter, const int* arrPageIndex, unsigned int unLength)
CPdfEditor::CPdfEditor(CPdfReader* _pReader, CPdfWriter* _pWriter)
{
m_pReader = _pReader;
m_pWriter = _pWriter;
@ -925,39 +926,16 @@ CPdfEditor::CPdfEditor(CPdfReader* _pReader, CPdfWriter* _pWriter, const int* ar
m_nError = 1;
return;
}
// Страницы должны быть созданы заранее для ссылки
Catalog* pCatalog = pPDFDocument->getCatalog();
for (int i = 0; i < unLength; ++i)
{
Ref* pPageRef = pCatalog->getPageRef(arrPageIndex[i] + 1);
if (pPageRef->num == 0)
{
m_nError = 3;
return;
}
PdfWriter::CPage* pPage = new PdfWriter::CPage(pDoc);
pDoc->AddObject(pPage);
pDoc->AddPage(pPage);
// Получение объекта страницы
Object pageRefObj, pageObj;
pageRefObj.initRef(pPageRef->num, pPageRef->gen);
if (!pageRefObj.fetch(xref, &pageObj)->isDict())
{
pageObj.free();
pageRefObj.free();
m_nError = 3;
return;
}
m_mSplitUniqueRef[pPageRef->num] = pPage;
pageRefObj.free();
}
}
int CPdfEditor::Close(const std::wstring& wsPath)
{
m_wsDstFile = wsPath;
Close();
return 0;
}
void CPdfEditor::Close()
{
if (m_nMode != 0)
if (m_nMode != 0 || m_wsDstFile.empty())
return;
PDFDoc* pPDFDocument = m_pReader->GetPDFDocument();
PdfWriter::CDocument* pDoc = m_pWriter->GetDocument();
@ -1024,14 +1002,13 @@ void CPdfEditor::Close()
}
info.free();
if (!m_pWriter->EditClose() || !pDoc->AddToFile(pXref, pTrailer, pInfoXref, pInfoDict))
if (!m_pWriter->EditClose() || !pDoc->AddToFile(m_wsDstFile, pXref, pTrailer, pInfoXref, pInfoDict))
{
RELEASEOBJECT(pXref);
return;
}
std::wstring wsPath = pDoc->GetEditPdfPath();
std::string sPathUtf8New = U_TO_UTF8(wsPath);
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))
{
@ -1305,6 +1282,127 @@ bool CPdfEditor::EditPage(int nPageIndex, bool bSet)
RELEASEOBJECT(pXref);
return false;
}
bool CPdfEditor::SplitPages(const int* arrPageIndex, unsigned int unLength)
{
PDFDoc* pPDFDocument = m_pReader->GetPDFDocument();
XRef* xref = pPDFDocument->getXRef();
PdfWriter::CDocument* pDoc = m_pWriter->GetDocument();
// Страницы должны быть созданы заранее для ссылки
Catalog* pCatalog = pPDFDocument->getCatalog();
for (int i = 0; i < unLength; ++i)
{
Ref* pPageRef = pCatalog->getPageRef(arrPageIndex[i] + 1);
if (pPageRef->num == 0)
{
m_nError = 3;
return false;
}
PdfWriter::CPage* pPage = new PdfWriter::CPage(pDoc);
pDoc->AddObject(pPage);
pDoc->AddPage(pPage);
// Получение объекта страницы
Object pageRefObj, pageObj;
pageRefObj.initRef(pPageRef->num, pPageRef->gen);
if (!pageRefObj.fetch(xref, &pageObj)->isDict())
{
pageObj.free();
pageRefObj.free();
m_nError = 3;
return false;
}
m_mSplitUniqueRef[pPageRef->num] = pPage;
pageRefObj.free();
}
bool bRes = true;
for (unsigned int i = 0; i < unLength; ++i)
bRes &= SplitPage(arrPageIndex[i]);
if (!bRes)
return false;
Object oCatalog;
if (!xref->getCatalog(&oCatalog)->isDict())
{
oCatalog.free();
return false;
}
Object oAcroForm;
if (oCatalog.dictLookupNF("AcroForm", &oAcroForm)->isRef() || oAcroForm.isDict())
{
PdfWriter::CDictObject* pAcroForm = new PdfWriter::CDictObject();
if (oAcroForm.isRef())
{
pDoc->AddObject(pAcroForm);
oAcroForm.free();
if (!oCatalog.dictLookup("AcroForm", &oAcroForm)->isDict())
{
oAcroForm.free(); oCatalog.free();
return false;
}
}
pDoc->SetAcroForm(pAcroForm);
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();
std::map<int, PdfWriter::CObjectBase*>::iterator it = m_mSplitUniqueRef.find(oFieldsRef.num);
if (oFieldsRef.num > 0 && it != m_mSplitUniqueRef.end())
{
pAcroForm->Add(chKey, it->second);
continue;
}
if (oAcroForm.dictGetVal(nIndex, &oTemp)->isArray())
{
PdfWriter::CArrayObject* pArray = new PdfWriter::CArrayObject();
if (oFieldsRef.num > 0)
{
pDoc->AddObject(pArray);
m_mSplitUniqueRef[oFieldsRef.num] = pArray;
}
pAcroForm->Add(chKey, pArray);
for (int nIndex = 0; nIndex < oTemp.arrayGetLength(); ++nIndex)
{
Object oRes;
if (oTemp.arrayGetNF(nIndex, &oRes)->isRef())
{
it = m_mSplitUniqueRef.find(oRes.getRefNum());
if (it != m_mSplitUniqueRef.end())
pArray->Add(it->second);
}
oRes.free();
}
oTemp.free();
continue;
}
else
{
oTemp.free();
oAcroForm.dictGetValNF(nIndex, &oTemp);
}
}
else
oAcroForm.dictGetValNF(nIndex, &oTemp);
PdfWriter::CObjectBase* pBase = DictToCDictObject2(&oTemp, pDoc, xref, m_mSplitUniqueRef);
pAcroForm->Add(chKey, pBase);
oTemp.free();
}
}
oAcroForm.free(); oCatalog.free();
return bRes;
}
bool CPdfEditor::SplitPage(int nPageIndex)
{
PDFDoc* pPDFDocument = m_pReader->GetPDFDocument();
@ -1394,91 +1492,6 @@ bool CPdfEditor::SplitPage(int nPageIndex)
return true;
}
void CPdfEditor::SplitEnd()
{
PDFDoc* pPDFDocument = m_pReader->GetPDFDocument();
XRef* xref = pPDFDocument->getXRef();
PdfWriter::CDocument* pDoc = m_pWriter->GetDocument();
Object oCatalog;
if (!xref->getCatalog(&oCatalog)->isDict())
{
oCatalog.free();
return;
}
Object oAcroForm;
if (oCatalog.dictLookupNF("AcroForm", &oAcroForm)->isRef() || oAcroForm.isDict())
{
PdfWriter::CDictObject* pAcroForm = new PdfWriter::CDictObject();
if (oAcroForm.isRef())
{
pDoc->AddObject(pAcroForm);
oAcroForm.free();
if (!oCatalog.dictLookup("AcroForm", &oAcroForm)->isDict())
{
oAcroForm.free(); oCatalog.free();
return;
}
}
pDoc->SetAcroForm(pAcroForm);
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();
std::map<int, PdfWriter::CObjectBase*>::iterator it = m_mSplitUniqueRef.find(oFieldsRef.num);
if (oFieldsRef.num > 0 && it != m_mSplitUniqueRef.end())
{
pAcroForm->Add(chKey, it->second);
continue;
}
if (oAcroForm.dictGetVal(nIndex, &oTemp)->isArray())
{
PdfWriter::CArrayObject* pArray = new PdfWriter::CArrayObject();
if (oFieldsRef.num > 0)
{
pDoc->AddObject(pArray);
m_mSplitUniqueRef[oFieldsRef.num] = pArray;
}
pAcroForm->Add(chKey, pArray);
for (int nIndex = 0; nIndex < oTemp.arrayGetLength(); ++nIndex)
{
Object oRes;
if (oTemp.arrayGetNF(nIndex, &oRes)->isRef())
{
it = m_mSplitUniqueRef.find(oRes.getRefNum());
if (it != m_mSplitUniqueRef.end())
pArray->Add(it->second);
}
oRes.free();
}
oTemp.free();
continue;
}
else
{
oTemp.free();
oAcroForm.dictGetValNF(nIndex, &oTemp);
}
}
else
oAcroForm.dictGetValNF(nIndex, &oTemp);
PdfWriter::CObjectBase* pBase = DictToCDictObject2(&oTemp, pDoc, xref, m_mSplitUniqueRef);
pAcroForm->Add(chKey, pBase);
oTemp.free();
}
}
oAcroForm.free(); oCatalog.free();
}
bool CPdfEditor::DeletePage(int nPageIndex)
{
return m_pWriter->GetDocument()->DeletePage(nPageIndex);

View File

@ -41,13 +41,12 @@ class CPdfEditor
{
public:
CPdfEditor(const std::wstring& _wsSrcFile, const std::wstring& _wsPassword, CPdfReader* _pReader, const std::wstring& _wsDstFile, CPdfWriter* _pWriter);
CPdfEditor(CPdfReader* _pReader, CPdfWriter* _pWriter, const int* arrPageIndex, unsigned int unLength);
CPdfEditor(CPdfReader* _pReader, CPdfWriter* _pWriter);
int GetError();
void Close();
int Close(const std::wstring& wsPath);
bool EditPage(int nPageIndex, bool bSet = true);
bool SplitPage(int nPageIndex);
void SplitEnd();
bool DeletePage(int nPageIndex);
bool AddPage(int nPageIndex);
bool EditAnnot(int nPageIndex, int nID);
@ -62,10 +61,14 @@ public:
void EndMarkedContent();
bool IsBase14(const std::wstring& wsFontName, bool& bBold, bool& bItalic, std::wstring& wsFontPath);
bool SplitPage(int nPageIndex);
bool SplitPages(const int* arrPageIndex, unsigned int unLength);
private:
void GetPageTree(XRef* xref, Object* pPagesRefObj, PdfWriter::CPageTree* pPageParent = NULL);
std::wstring m_wsSrcFile;
std::wstring m_wsDstFile;
std::wstring m_wsPassword;
std::map<std::wstring, std::wstring> m_mFonts;
std::map<int, PdfWriter::CObjectBase*> m_mSplitUniqueRef; // map уникальных объектов для Split

View File

@ -165,10 +165,6 @@ bool CPdfFile::AddPage(int nPageIndex)
return false;
return m_pInternal->pEditor->AddPage(nPageIndex);
}
bool CPdfFile::SplitPages(const std::vector<int>& arrPageIndex)
{
return SplitPages(arrPageIndex.data(), arrPageIndex.size());
}
bool CPdfFile::SplitPages(const int* arrPageIndex, unsigned int unLength)
{
if (!m_pInternal->pEditor)
@ -179,15 +175,46 @@ bool CPdfFile::SplitPages(const int* arrPageIndex, unsigned int unLength)
if (!m_pInternal->pWriter)
m_pInternal->pWriter = new CPdfWriter(m_pInternal->pAppFonts, false, this);
m_pInternal->pEditor = new CPdfEditor(m_pInternal->pReader, m_pInternal->pWriter, arrPageIndex, unLength);
m_pInternal->pEditor = new CPdfEditor(m_pInternal->pReader, m_pInternal->pWriter);
if (m_pInternal->pEditor->GetError() != 0)
return false;
}
bool bRes = true;
for (unsigned int i = 0; i < unLength; ++i)
bRes &= m_pInternal->pEditor->SplitPage(arrPageIndex[i]);
if (bRes)
m_pInternal->pEditor->SplitEnd();
return m_pInternal->pEditor->SplitPages(arrPageIndex, unLength);
}
bool CPdfFile::MergePages(CPdfFile* pMergeFile, const int* arrPageIndex, unsigned int unLength, int nMergePos)
{
if (!pMergeFile || !pMergeFile->m_pInternal || !pMergeFile->m_pInternal->pReader)
return false;
if (!m_pInternal->pEditor)
{
if (!m_pInternal->pReader)
return false;
if (!m_pInternal->pWriter)
m_pInternal->pWriter = new CPdfWriter(m_pInternal->pAppFonts, false, this);
m_pInternal->pEditor = new CPdfEditor(m_pInternal->wsSrcFile, m_pInternal->wsPassword, m_pInternal->pReader, L"", m_pInternal->pWriter);
if (m_pInternal->pEditor->GetError() != 0)
return false;
}
// TODO
return m_pInternal->pEditor->SplitPages(arrPageIndex, unLength);
}
bool CPdfFile::MergePages(const std::wstring& wsPath, const std::wstring& wsPassword, const int* arrPageIndex, unsigned int unLength, int nMergePos)
{
CPdfFile* pMergeFile = new CPdfFile(m_pInternal->pAppFonts);
pMergeFile->SetTempDirectory(m_pInternal->wsTempFolder);
if (!pMergeFile->LoadFromFile(wsPath, L"", wsPassword, wsPassword))
{
RELEASEOBJECT(pMergeFile);
return false;
}
bool bRes = MergePages(pMergeFile, arrPageIndex, unLength, nMergePos);
RELEASEOBJECT(pMergeFile);
return bRes;
}
HRESULT CPdfFile::ChangePassword(const std::wstring& wsPath, const std::wstring& wsPassword)
@ -503,6 +530,8 @@ int CPdfFile::SaveToFile(const std::wstring& wsPath)
{
if (!m_pInternal->pWriter)
return 1;
if (m_pInternal->pEditor)
return m_pInternal->pEditor->Close(wsPath);
return m_pInternal->pWriter->SaveToFile(wsPath);
}
void CPdfFile::SetPassword(const std::wstring& wsPassword)

View File

@ -94,11 +94,12 @@ public:
// Переходит в режим редактирования. Pdf уже должен быть открыт на чтение - LoadFromFile/LoadFromMemory
bool EditPdf(const std::wstring& wsDstFile = L"");
// Манипуляции со страницами возможны в режиме редактирования
bool EditPage (int nPageIndex);
bool DeletePage (int nPageIndex);
bool AddPage (int nPageIndex);
bool SplitPages (const std::vector<int>& arrPageIndex);
bool SplitPages (const int* arrPageIndex, unsigned int unLength);
bool EditPage (int nPageIndex);
bool DeletePage(int nPageIndex);
bool AddPage (int nPageIndex);
bool SplitPages(const int* arrPageIndex, unsigned int unLength);
bool MergePages(CPdfFile* pMergeFile, const int* arrPageIndex = NULL, unsigned int unLength = 0, int nMergePos = -1);
bool MergePages(const std::wstring& wsPath, const std::wstring& wsPassword = L"", const int* arrPageIndex = NULL, unsigned int unLength = 0, int nMergePos = -1);
HRESULT ChangePassword(const std::wstring& wsPath, const std::wstring& wsPassword = L"");
#endif

View File

@ -203,8 +203,6 @@ namespace PdfWriter
m_pFieldsResources = NULL;
memset((void*)m_sTTFontTag, 0x00, 8);
m_pDefaultCheckBoxFont = NULL;
m_wsDocumentID = L"";
m_wsFilePath = L"";
m_vExtGrStates.clear();
m_vStrokeAlpha.clear();
@ -1402,7 +1400,7 @@ namespace PdfWriter
return true;
}
bool CDocument::EditPdf(const std::wstring& wsPath, int nPosLastXRef, int nSizeXRef, CXref* pXref, CCatalog* pCatalog, CEncryptDict* pEncrypt, int nFormField)
bool CDocument::EditPdf(int nPosLastXRef, int nSizeXRef, CXref* pXref, CCatalog* pCatalog, CEncryptDict* pEncrypt, int nFormField)
{
if (!pXref || !pCatalog)
return false;
@ -1435,7 +1433,6 @@ namespace PdfWriter
}
m_unFormFields = nFormField;
m_wsFilePath = wsPath;
return true;
}
bool CDocument::EditResources(CXref* pXref, CResourcesDict* pResources)
@ -1610,16 +1607,16 @@ namespace PdfWriter
}
return false;
}
bool CDocument::AddToFile(CXref* pXref, CDictObject* pTrailer, CXref* pInfoXref, CInfoDict* pInfo)
bool CDocument::AddToFile(const std::wstring& wsPath, CXref* pXref, CDictObject* pTrailer, CXref* pInfoXref, CInfoDict* pInfo)
{
if (!pTrailer || m_wsFilePath.empty())
if (!pTrailer || wsPath.empty())
return false;
CFileStream* pStream = new CFileStream();
if (!pStream)
return false;
if (!pStream->OpenFile(m_wsFilePath, false))
if (!pStream->OpenFile(wsPath, false))
{
RELEASEOBJECT(pStream);
return false;
@ -1708,7 +1705,7 @@ namespace PdfWriter
RELEASEOBJECT(pStream);
unsigned int nSizeXRef = m_pXref->GetSizeXRef();
m_pXref = m_pLastXref;
Sign(m_wsFilePath, nSizeXRef, bNeedStreamXRef);
Sign(wsPath, nSizeXRef, bNeedStreamXRef);
RELEASEOBJECT(m_pEncryptDict);
return true;

View File

@ -190,16 +190,15 @@ namespace PdfWriter
void SetCurImage(CImageDict* pImage) { m_pCurImage = pImage; }
bool CreatePageTree(CXref* pXref, CPageTree* pPageTree);
bool EditPdf(const std::wstring& wsPath, int nPosLastXRef, int nSizeXRef, CXref* pXref, CCatalog* pCatalog, CEncryptDict* pEncrypt, int nFormField);
bool EditPdf(int nPosLastXRef, int nSizeXRef, CXref* pXref, CCatalog* pCatalog, CEncryptDict* pEncrypt, int nFormField);
bool EditResources(CXref* pXref, CResourcesDict* pResources);
std::pair<int, int> GetPageRef(int nPageIndex);
bool EditPage(CXref* pXref, CPage* pPage, int nPageIndex);
CPage* AddPage(int nPageIndex);
bool DeletePage(int nPageIndex);
bool AddToFile(CXref* pXref, CDictObject* pTrailer, CXref* pInfoXref, CInfoDict* pInfo);
bool AddToFile(const std::wstring& wsPath, CXref* pXref, CDictObject* pTrailer, CXref* pInfoXref, CInfoDict* pInfo);
void AddObject(CObjectBase* pObj);
void Sign(const TRect& oRect, CImageDict* pImage, ICertificate* pCert);
std::wstring GetEditPdfPath() { return m_wsFilePath; }
bool EditAnnot (CXref* pXref, CAnnotation* pAnnot, int nID);
bool EditParent(CXref* pXref, CDictObject* pParent, int nID);
bool DeleteAnnot(int nObjNum, int nObjGen);
@ -309,7 +308,6 @@ namespace PdfWriter
FT_Library m_pFreeTypeLibrary;
bool m_bPDFAConformance;
std::wstring m_wsDocumentID;
std::wstring m_wsFilePath;
CDictObject* m_pAcroForm;
CResourcesDict* m_pFieldsResources;
std::vector<CRadioGroupField*> m_vRadioGroups;

View File

@ -356,14 +356,21 @@ TEST_F(CPdfFileTest, SplitPdf)
LoadFromFile();
pdfFile->SplitPages({2, 5, 6, 7});
std::vector<int> arrPages = { 0, 1, 2, 3 };
pdfFile->SplitPages(arrPages.empty() ? NULL : arrPages.data(), arrPages.size());
pdfFile->SaveToFile(wsDstFile);
}
TEST_F(CPdfFileTest, MergePdf)
{
GTEST_SKIP();
//GTEST_SKIP();
LoadFromFile();
pdfFile->MergePages(wsSrcFile);
pdfFile->SaveToFile(wsDstFile);
}
TEST_F(CPdfFileTest, CopyAnotherPdf)