From 7b264e940df4753a4e5ada4472a155e6ca831c26 Mon Sep 17 00:00:00 2001 From: Svetlana Kulikova Date: Wed, 9 Apr 2025 15:38:21 +0300 Subject: [PATCH] Merge Outlines two files --- PdfFile/PdfEditor.cpp | 142 +++++++++++++++++++++++++++++- PdfFile/PdfEditor.h | 2 +- PdfFile/PdfFile.cpp | 5 +- PdfFile/PdfFile.h | 2 +- PdfFile/PdfReader.cpp | 4 +- PdfFile/SrcWriter/Destination.cpp | 26 +++--- PdfFile/SrcWriter/Destination.h | 4 +- PdfFile/SrcWriter/Document.cpp | 2 +- PdfFile/SrcWriter/Document.h | 3 +- PdfFile/test/test.cpp | 36 ++++++-- 10 files changed, 192 insertions(+), 34 deletions(-) diff --git a/PdfFile/PdfEditor.cpp b/PdfFile/PdfEditor.cpp index 177dd732b0..6d65e41b29 100644 --- a/PdfFile/PdfEditor.cpp +++ b/PdfFile/PdfEditor.cpp @@ -40,12 +40,16 @@ #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 "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" #define AddToObject(oVal)\ {\ @@ -929,7 +933,7 @@ CPdfEditor::CPdfEditor(const std::wstring& _wsSrcFile, const std::wstring& _wsPa } bool CPdfEditor::IncrementalUpdates() { - if (m_nMode == Mode::Unknown) + if (m_nMode != Mode::Unknown) return true; m_nMode = Mode::WriteAppend; @@ -1157,7 +1161,7 @@ void CPdfEditor::Close() if (m_wsDstFile.empty()) return; - if (m_nMode == Mode::WriteAppend) + if (m_nMode != Mode::WriteAppend) { m_pWriter->SaveToFile(m_wsDstFile); return; @@ -1894,7 +1898,106 @@ BYTE* CPdfEditor::SplitPages(const int* arrPageIndex, unsigned int unLength) RELEASEARRAYOBJECTS(pRes); return NULL; } -bool CPdfEditor::MergePages(const int* arrPageIndex, unsigned int unLength) +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::MergePages(const std::wstring& wsPath, const int* arrPageIndex, unsigned int unLength) { if (m_nMode != Mode::WriteAppend && !IncrementalUpdates()) return false; @@ -1904,6 +2007,39 @@ bool CPdfEditor::MergePages(const int* arrPageIndex, unsigned int unLength) 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) diff --git a/PdfFile/PdfEditor.h b/PdfFile/PdfEditor.h index aefb3f68a8..e548a75668 100644 --- a/PdfFile/PdfEditor.h +++ b/PdfFile/PdfEditor.h @@ -94,7 +94,7 @@ public: bool IsBase14(const std::wstring& wsFontName, bool& bBold, bool& bItalic, std::wstring& wsFontPath); BYTE* SplitPages(const int* arrPageIndex, unsigned int unLength); - bool MergePages(const int* arrPageIndex, unsigned int unLength); + bool MergePages(const std::wstring& wsPath, const int* arrPageIndex, unsigned int unLength); private: void GetPageTree(XRef* xref, Object* pPagesRefObj, PdfWriter::CPageTree* pPageParent = NULL); diff --git a/PdfFile/PdfFile.cpp b/PdfFile/PdfFile.cpp index d26eb57446..27e0cb68c5 100644 --- a/PdfFile/PdfFile.cpp +++ b/PdfFile/PdfFile.cpp @@ -32,13 +32,12 @@ #include "PdfFile.h" #include "PdfWriter.h" #include "PdfReader.h" +#include "PdfEditor.h" #include "../DesktopEditor/common/File.h" #include "../DesktopEditor/graphics/commands/DocInfo.h" -#include "lib/xpdf/PDFDoc.h" #include "Resources/BaseFonts.h" -#include "PdfEditor.h" #include "OnlineOfficeBinToPdf.h" #include "SrcWriter/Document.h" @@ -141,7 +140,7 @@ bool CPdfFile::MergePages(const std::wstring& wsPath, const std::wstring& wsPass if (!m_pInternal->pEditor) return false; if (m_pInternal->pReader->MergePages(wsPath, wsPassword)) - return m_pInternal->pEditor->MergePages(arrPageIndex, unLength); + return m_pInternal->pEditor->MergePages(wsPath, arrPageIndex, unLength); return false; } bool CPdfFile::MovePage(int nPageIndex, int nPos) diff --git a/PdfFile/PdfFile.h b/PdfFile/PdfFile.h index 8e2f3b2cca..0985f3eed9 100644 --- a/PdfFile/PdfFile.h +++ b/PdfFile/PdfFile.h @@ -126,8 +126,8 @@ public: virtual BYTE* GetStructure(); virtual BYTE* GetLinks(int nPageIndex); - bool MergePages(BYTE* data, DWORD length); bool ValidMetaData(); + bool MergePages(BYTE* data, DWORD length); int GetRotate(int nPageIndex); int GetMaxRefID(); BYTE* GetWidgets(); diff --git a/PdfFile/PdfReader.cpp b/PdfFile/PdfReader.cpp index c2cabea6bd..644cc73a11 100644 --- a/PdfFile/PdfReader.cpp +++ b/PdfFile/PdfReader.cpp @@ -961,13 +961,13 @@ BYTE* CPdfReader::GetStructure() for (int iPDF = 0; iPDF < m_vPDFContext.size(); ++iPDF) { PDFDoc* pDoc = m_vPDFContext[iPDF]->m_pDocument; - if (!pDoc->getOutline()) + Outline* pOutline = pDoc->getOutline(); + if (!pOutline) { nStartPage += pDoc->getNumPages(); continue; } - Outline* pOutline = pDoc->getOutline(); GList* pList = pOutline->getItems(); if (!pList) { diff --git a/PdfFile/SrcWriter/Destination.cpp b/PdfFile/SrcWriter/Destination.cpp index 2f2a342923..bd63c6709f 100644 --- a/PdfFile/SrcWriter/Destination.cpp +++ b/PdfFile/SrcWriter/Destination.cpp @@ -30,20 +30,19 @@ * */ #include "Destination.h" -#include "Pages.h" namespace PdfWriter { //---------------------------------------------------------------------------------------- // CDestination //---------------------------------------------------------------------------------------- - CDestination::CDestination(CPage* pPage, CXref* pXref, bool bInline) + CDestination::CDestination(CObjectBase* pPage, CXref* pXref, bool bInline) { if (!bInline) pXref->Add(this); // Первый элемент массива должен быть страницей, которой принадлежит объект - Add((CObjectBase*)pPage); + Add(pPage); Add("Fit"); // Значение по умолчанию Fit } bool CDestination::IsValid() const @@ -51,21 +50,28 @@ namespace PdfWriter if (m_arrList.size() < 2) return false; - CObjectBase* pObject = Get(0, false); - if ((object_type_DICT != pObject->GetType() || dict_type_PAGE != ((CDictObject*)pObject)->GetDictType()) && - (object_type_PROXY != pObject->GetType() || object_type_DICT != ((CProxyObject*)pObject)->Get()->GetType() || dict_type_PAGE != ((CDictObject*)((CProxyObject*)pObject)->Get())->GetDictType())) - return false; + // Проверка, что объект является страницей. Но это может быть ссылка на нередактируемую страницу + // CObjectBase* pObject = Get(0, false); + // if ((object_type_DICT != pObject->GetType() || dict_type_PAGE != ((CDictObject*)pObject)->GetDictType()) && + // (object_type_PROXY != pObject->GetType() || object_type_DICT != ((CProxyObject*)pObject)->Get()->GetType() || dict_type_PAGE != ((CDictObject*)((CProxyObject*)pObject)->Get())->GetDictType())) + // return false; return true; } void CDestination::PrepareArray() { - CPage* pPage = (CPage*)Get(0); - if (m_arrList.size() > 1) { + CObjectBase* pPage = Get(0); + if (pPage->GetType() != object_type_DICT) + { + CObjectBase* pCopy = pPage->Copy(); + pCopy->SetRef(pPage->GetObjId(), pPage->GetGenNo()); + pPage = new CProxyObject(pCopy, true); + } + Clear(); - Add((CObjectBase*)pPage); + Add(pPage); } } void CDestination::SetXYZ(float fLeft, float fTop, float fZoom) diff --git a/PdfFile/SrcWriter/Destination.h b/PdfFile/SrcWriter/Destination.h index d0e73bb6eb..ea5bfec429 100644 --- a/PdfFile/SrcWriter/Destination.h +++ b/PdfFile/SrcWriter/Destination.h @@ -36,12 +36,10 @@ namespace PdfWriter { - class CPage; - class CDestination : public CArrayObject { public: - CDestination(CPage* pPage, CXref* pXref, bool bInline = false); + CDestination(CObjectBase* pPage, CXref* pXref, bool bInline = false); bool IsValid() const; void SetXYZ (float fLeft, float fTop, float fZoom); void SetFit (); diff --git a/PdfFile/SrcWriter/Document.cpp b/PdfFile/SrcWriter/Document.cpp index 2e4d48bb17..4219c8cb8f 100644 --- a/PdfFile/SrcWriter/Document.cpp +++ b/PdfFile/SrcWriter/Document.cpp @@ -527,7 +527,7 @@ namespace PdfWriter return new COutline(pParent, sTitle, m_pXref); } - CDestination* CDocument::CreateDestination(CPage* pPage, bool bInline) + CDestination* CDocument::CreateDestination(CObjectBase* pPage, bool bInline) { if (pPage) return new CDestination(pPage, m_pXref, bInline); diff --git a/PdfFile/SrcWriter/Document.h b/PdfFile/SrcWriter/Document.h index ffa26461ef..fb0fee9841 100644 --- a/PdfFile/SrcWriter/Document.h +++ b/PdfFile/SrcWriter/Document.h @@ -130,7 +130,8 @@ namespace PdfWriter void AddPageLabel(EPageNumStyle eStyle, unsigned int unFirstPage, const char* sPrefix); void AddPageLabel(unsigned int unPageIndex, EPageNumStyle eStyle, unsigned int unFirstPage, const char* sPrefix); COutline* CreateOutline(COutline* pParent, const char* sTitle); - CDestination* CreateDestination(CPage* pPage, bool bInline = false); + COutline* GetOutlines() { return m_pOutlines; } + CDestination* CreateDestination(CObjectBase* pPage, bool bInline = false); bool AddMetaData(const std::wstring& sMetaName, BYTE* pMetaData, DWORD nMetaLength); CExtGrState* GetExtGState(double dAlphaStroke = -1, double dAlphaFill = -1, EBlendMode eMode = blendmode_Unknown, int nStrokeAdjustment = -1); diff --git a/PdfFile/test/test.cpp b/PdfFile/test/test.cpp index 9e4d71a908..3b539e26ee 100644 --- a/PdfFile/test/test.cpp +++ b/PdfFile/test/test.cpp @@ -90,13 +90,13 @@ public: RELEASEOBJECT(oWorker); } - void LoadFromFile() + void LoadFromFile(const std::wstring& _wsSrcFile = L"") { - bool bResult = pdfFile->LoadFromFile(wsSrcFile); + bool bResult = pdfFile->LoadFromFile(_wsSrcFile.empty() ? wsSrcFile : _wsSrcFile); if (!bResult) { std::wstring wsPassword = L"123456"; - bResult = pdfFile->LoadFromFile(wsSrcFile, L"", wsPassword, wsPassword); + bResult = pdfFile->LoadFromFile(_wsSrcFile.empty() ? wsSrcFile : _wsSrcFile, L"", wsPassword, wsPassword); } ASSERT_TRUE(bResult); @@ -350,21 +350,39 @@ TEST_F(CPdfFileTest, VerifySign) RELEASEOBJECT(pCertificate); } -TEST_F(CPdfFileTest, MergePdf) +TEST_F(CPdfFileTest, SplitPdf) { GTEST_SKIP(); LoadFromFile(); - std::vector arrPages = { 0, 1 }; + std::vector arrPages = { 0 }; BYTE* pFile = pdfFile->SplitPages(arrPages.data(), arrPages.size()); + ASSERT_TRUE(pFile != NULL); + + NSFile::CFileBinary oFile; + std::wstring wsSplitFile = NSFile::GetProcessDirectory() + L"/test_split.pdf"; + if (oFile.CreateFileW(wsSplitFile)) + { + int nLength = pFile[0] | pFile[1] << 8 | pFile[2] << 16 | pFile[3] << 24; + oFile.WriteFile(pFile + 4, nLength); + } + oFile.CloseFile(); + + RELEASEARRAYOBJECTS(pFile); +} + +TEST_F(CPdfFileTest, MergePdf) +{ + // GTEST_SKIP(); + + LoadFromFile(); ASSERT_TRUE(pdfFile->EditPdf(wsDstFile)); - pdfFile->MergePages(wsDstFile); + std::wstring wsSplitFile = NSFile::GetProcessDirectory() + L"/test_split.pdf"; + pdfFile->MergePages(wsSplitFile); pdfFile->Close(); - - RELEASEARRAYOBJECTS(pFile); } TEST_F(CPdfFileTest, EditPdf) @@ -389,7 +407,7 @@ TEST_F(CPdfFileTest, EditPdf) TEST_F(CPdfFileTest, EditPdfFromBase64) { - // GTEST_SKIP(); + GTEST_SKIP(); NSFonts::NSApplicationFontStream::SetGlobalMemoryStorage(NSFonts::NSApplicationFontStream::CreateDefaultGlobalMemoryStorage());