Скопируйте только необходимые объекты из файла PDF - PullRequest
2 голосов
/ 21 марта 2019

У меня есть огромный PDF-файл с более чем 100 страницами, и я хочу разделить их на отдельные PDF-файлы (содержащие только одну страницу каждый). Проблема в том, что PoDoFo копирует не только страницу, но и весь документ из-за ссылок (и поэтому каждый из 100 файлов PDF имеет такой же размер, как и 100-страничный PDF). Соответствующее сообщение в списке рассылки можно найти, к сожалению, решение не предоставлено.

В исходном коде функции InsertPages есть объяснение:

Эта функция работает немного иначе, чем можно было бы ожидать. Вместо того, чтобы копировать одну страницу за раз - мы копируем ВЕСЬ документ а затем удалите страницы, которые нам не интересны.

Мы делаем это, потому что
1) ЗНАЧИТЕЛЬНО упрощает процесс
2) Гарантирует, что общие объекты не копируются несколько раз
3) предлагает НАМНОГО более быструю работу в обычных случаях

ОДНАКО: потому что PoDoFo в настоящее время не делает никаких «объектов» сборка мусора "во время записи () - мы получим больше документы, так как данные с неиспользуемых страниц также будут там.

Я пробовал несколько способов скопировать только релевантные объекты, но каждый из них не удался.

  • Скопируйте все страницы и удалите ненужные
  • Использовать упаковку XObject: FillXObjectFromDocumentPage и FillXObjectFromExistingPage
  • Копирование объекта по объекту
  • Используйте RenumberObjects с bDoGarbageCollection = true

но ни один из них не сработал. У кого-нибудь есть идея или рабочее решение этой проблемы?

Ответы [ 2 ]

2 голосов
/ 25 июня 2019

Единственное решение - использовать другую библиотеку 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 есть, вы просто не видите его части.

0 голосов
/ 01 июля 2019

Деннис из службы поддержки вдохновил меня, поэтому я опубликую его как ответ, даже если я еще не тестировал его.Как только я это сделаю, я обновлю ответ правильным решением.

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;
            }
        }
    }
}
...