簡體   English   中英

僅從 PDF 文件復制必要的對象

[英]Copy only necessary objects from PDF file

我有一個超過 100 頁的巨大 PDF 文件,我想將它們分成單個 PDF 文件(每個文件僅包含一頁)。 問題是,PoDoFo 不僅復制頁面,而且由於引用而復制整個文檔(因此 100 個 PDF 文件中的每一個都與 100 頁 PDF 具有相同的大小)。 可以找到相關的郵件列表帖子,遺憾的是沒有提供解決方案。

在函數InsertPages源代碼中有解釋:

此功能的工作方式與人們的預期略有不同。 而不是一次復制一頁 - 我們復制整個文檔,然后刪除我們不感興趣的頁面。

我們這樣做是因為
1) 顯着簡化流程
2) 保證共享對象不會被多次復制
3)為常見情況提供更快的性能

但是:因為 PoDoFo 當前在 Write() 期間不執行任何類型的“對象垃圾收集” - 我們最終會得到更大的文檔,因為來自未使用頁面的數據也將在那里。

我嘗試了幾種方法來僅復制相關對象,但每個方法都失敗了。

  • 復制所有頁面並刪除不相關的頁面
  • 使用 XObject 包裝: FillXObjectFromDocumentPageFillXObjectFromExistingPage
  • 逐個對象復制
  • 使用RenumberObjectsbDoGarbageCollection = true

但他們都沒有成功。 有人對這個問題有想法或可行的解決方案嗎?

唯一的解決方案是使用另一個 PDF 庫。 或者等待垃圾收集被實施。

您提到的報價中說明了問題:

> during a Write() - we will end up with larger documents, since the
> data from unused pages will also be in there.

這意味着 podofo 始終將整個 PDF 內容放入您的文件中,無論如何。 整個 PDF 都在那里,你只是看不到它的一部分。

來自支持的 Dennis 給我發送了一個優化版本的InsertPages函數的工作示例,它實際上修復了頁面引用並顯着減小了文檔大小!

void PdfMemDocument::InsertPages2(const PdfMemDocument & rDoc, std::vector<int> pageNumbers)
{
    std::unordered_set<PdfObject*> totalSet;
    std::vector<pdf_objnum> oldObjNumPages;
    std::unordered_map<pdf_objnum, pdf_objnum> oldObjNumToNewObjNum;

    std::vector<PdfObject*> newPageObjects;

    // Collect all dependencies from all pages that are to be copied
    for (int i = 0; i < pageNumbers.size(); ++i) {
        PdfPage* page = rDoc.GetPage(pageNumbers[i]);
        if (page) {
            oldObjNumPages.push_back(page->GetObject()->Reference().ObjectNumber());
            std::unordered_set<PdfObject*> *set = page->GetPageDependencies();
            totalSet.insert(set->begin(), set->end());
            delete set;
        }
    }

    // Create a new page object for every copied page from the old document
    // Copy all objects the pages depend on to the new document
    for (auto it = totalSet.begin(); it != totalSet.end(); ++it) {
        unsigned int length = static_cast<unsigned int>(GetObjects().GetSize() + GetObjects().GetFreeObjects().size());
        PdfReference ref(static_cast<unsigned int>(length+1), 0);
        PdfObject* pObj = new PdfObject(ref, *(*it));
        pObj->SetOwner(&(GetObjects()));
        if ((*it)->HasStream()) {
            PdfStream *stream = (*it)->GetStream();
            pdf_long length;
            char* buf;
            stream->GetCopy(&buf, &length);
            PdfMemoryInputStream inputStream(buf, length);
            pObj->GetStream()->SetRawData(&inputStream, length);
            free(buf);

        }
        oldObjNumToNewObjNum.insert(std::pair<pdf_objnum, pdf_objnum>((*it)->Reference().ObjectNumber(), length+1));
        GetObjects().push_back(pObj);
        newPageObjects.push_back(pObj);
    }

    // In all copied objects, fix the object numbers so they are valid in the new document
    for (auto it = newPageObjects.begin(); it != newPageObjects.end(); ++it) {
        FixPageReferences(GetObjects(), *it, oldObjNumToNewObjNum);
    }

    // Insert the copied pages into the pages tree
    for (auto it = oldObjNumPages.begin(); it != oldObjNumPages.end(); ++it) {
        PdfObject* pageObject = GetObjects().GetObject(PdfReference(oldObjNumToNewObjNum[(*it)], 0));
        PdfPage *page = new PdfPage(pageObject, std::deque<PdfObject*>());
        GetPagesTree()->InsertPage(GetPageCount() - 1, page);
    }

}

std::unordered_set<PdfObject *>* PdfPage::GetPageDependencies() const
{
    std::unordered_set<PdfObject *> *set = new std::unordered_set<PdfObject *>();

    const PdfObject* pageObj = GetObject();
    if (pageObj) {
        PdfVecObjects* objects = pageObj->GetOwner();
        if (objects) {
            set->insert((PdfObject*)pageObj);
            objects->GetObjectDependencies2(pageObj, *set);
        }
    }

    return set;
}

// Optimized version of PdfVecObjects::GetObjectDependencies
void PdfVecObjects::GetObjectDependencies2(const PdfObject* pObj, std::unordered_set<PdfObject*> &refMap) const
{
    // Check objects referenced from this object
    if (pObj->IsReference())
    {
        PdfObject* referencedObject = GetObject(pObj->GetReference());
        if (referencedObject != NULL && refMap.count(referencedObject) < 1) {
            (refMap).insert((PdfObject *)referencedObject); // Insert referenced object
            GetObjectDependencies2((const PdfObject*)referencedObject, refMap);
        }
    }
    else {
        // Recursion
        if (pObj->IsArray())
        {
            PdfArray::const_iterator itArray = pObj->GetArray().begin();
            while (itArray != pObj->GetArray().end())
            {
                GetObjectDependencies2(&(*itArray), refMap);
                ++itArray;
            }
        }
        else if (pObj->IsDictionary())
        {
            TCIKeyMap itKeys = pObj->GetDictionary().GetKeys().begin();
            while (itKeys != pObj->GetDictionary().GetKeys().end())
            {
                if ((*itKeys).first != PdfName("Parent")) {
                    GetObjectDependencies2((*itKeys).second, refMap);
                }
                ++itKeys;
            }
        }
    }
}

void FixPageReferences(PdfVecObjects& objects, PdfObject* pObject, std::unordered_map<pdf_objnum, pdf_objnum>& oldNumToNewNum) {
    if( !pObject)
    {
        PODOFO_RAISE_ERROR( ePdfError_InvalidHandle );
    }
    if( pObject->IsDictionary() )
    {
        TKeyMap::iterator it = pObject->GetDictionary().GetKeys().begin();

        while( it != pObject->GetDictionary().GetKeys().end() )
        {
            if ((*it).first != PdfName("Parent")) {
                FixPageReferences(objects, (*it).second, oldNumToNewNum);
            }
            ++it;
        }
    }
    else if( pObject->IsArray() )
    {
        PdfArray::iterator it = pObject->GetArray().begin();

        while( it != pObject->GetArray().end() )
        {
            FixPageReferences(objects, &(*it), oldNumToNewNum),
            ++it;
        }
    }
    else if( pObject->IsReference() )
    {
        //PdfObject* referencedObj = objects.GetObject(pObject->GetReference());

        pdf_objnum oldnum = pObject->GetReference().ObjectNumber();
        pdf_objnum newnum = oldNumToNewNum[oldnum];

        if (!newnum) throw new std::exception("No new object number for old object number");

        *pObject = PdfReference(newnum, 0);

    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM