Final bugfixing of unite tool

This commit is contained in:
Jakub Melka 2020-11-01 15:44:22 +01:00
parent d17715b17e
commit e131ca769c
4 changed files with 164 additions and 0 deletions

View File

@ -20,6 +20,7 @@
#include "pdfconstants.h"
#include "pdfdocumentreader.h"
#include "pdfobjectutils.h"
#include "pdfnametreeloader.h"
#include <QBuffer>
#include <QPainter>
@ -798,6 +799,80 @@ void PDFDocumentBuilder::createDocumentParts(const std::vector<size_t>& parts)
mergeTo(root, objectFactory.takeObject());
}
void PDFDocumentBuilder::mergeNames(PDFObjectReference a, PDFObjectReference b)
{
PDFObject aObject = getObjectByReference(a);
// First object is null, do nothing
if (aObject.isNull())
{
setObject(a, getObjectByReference(b));
return;
}
// Jakub Melka: otherwise we must merge names tree
PDFObject bObject = getObjectByReference(b);
const PDFDictionary* aDict = getDictionaryFromObject(aObject);
const PDFDictionary* bDict = getDictionaryFromObject(bObject);
// Store keys
std::set<QByteArray> keys;
for (size_t i = 0; i < aDict->getCount(); ++i)
{
keys.insert(aDict->getKey(i).getString());
}
for (size_t i = 0; i < bDict->getCount(); ++i)
{
keys.insert(bDict->getKey(i).getString());
}
PDFObjectFactory factory;
factory.beginDictionary();
for (const QByteArray& key : keys)
{
auto getObject = [](const PDFObjectStorage*, const PDFObject& object) { return object; };
auto aMap = PDFNameTreeLoader<PDFObject>::parse(&m_storage, aDict->get(key), getObject);
auto bMap = PDFNameTreeLoader<PDFObject>::parse(&m_storage, bDict->get(key), getObject);
aMap.merge(qMove(bMap));
if (aMap.empty())
{
continue;
}
factory.beginDictionaryItem(key);
factory.beginDictionary();
factory.beginDictionaryItem("Names");
factory.beginArray();
for (const auto& item : aMap)
{
factory << WrapName(item.first);
factory << item.second;
}
factory.endArray();
factory.endDictionaryItem();
factory.beginDictionaryItem("Limits");
factory.beginArray();
factory << WrapName((*aMap.begin()).first);
factory << WrapName((*aMap.rbegin()).first);
factory.endArray();
factory.endDictionaryItem();
factory.endDictionary();
factory.endDictionaryItem();
}
factory.endDictionary();
setObject(a, factory.takeObject());
}
void PDFDocumentBuilder::appendTo(PDFObjectReference reference, PDFObject object)
{
m_storage.setObject(reference, PDFObjectManipulator::merge(m_storage.getObject(reference), qMove(object), PDFObjectManipulator::ConcatenateArrays));
@ -3458,6 +3533,20 @@ void PDFDocumentBuilder::updateTrailerDictionary(PDFInteger objectCount)
}
void PDFDocumentBuilder::setCatalogNames(PDFObjectReference names)
{
PDFObjectFactory objectBuilder;
objectBuilder.beginDictionary();
objectBuilder.beginDictionaryItem("Names");
objectBuilder << ((names.isValid()) ? PDFObject::createReference(names) : PDFObject());
objectBuilder.endDictionaryItem();
objectBuilder.endDictionary();
PDFObject updatedCatalog = objectBuilder.takeObject();
mergeTo(getCatalogReference(), updatedCatalog);
}
/* END GENERATED CODE */
} // namespace pdf

View File

@ -354,6 +354,12 @@ public:
/// \param parts Parts (page count of each document part)
void createDocumentParts(const std::vector<size_t>& parts);
/// Merges two independent 'Names' entry in catalog dictionary. It is used,
/// for example, when documents are being merged.
/// \param a First 'Names' entry
/// \param b Second 'Names' entry
void mergeNames(PDFObjectReference a, PDFObjectReference b);
/* START GENERATED CODE */
/// Appends a new page after last page.
@ -1082,6 +1088,11 @@ public:
void updateTrailerDictionary(PDFInteger objectCount);
/// Set reference to 'Names' dictionary to catalog.
/// \param names Reference to Names dictionary.
void setCatalogNames(PDFObjectReference names);
/* END GENERATED CODE */
private:

View File

@ -74,6 +74,7 @@ int PDFToolUnite::execute(const PDFToolOptions& options)
pdf::PDFObjectReference ocPropertiesMerged = documentBuilder.addObject(pdf::PDFObject());
pdf::PDFObjectReference formMerged = documentBuilder.addObject(pdf::PDFObject());
pdf::PDFObjectReference namesMerged = documentBuilder.addObject(pdf::PDFObject());
std::vector<pdf::PDFObjectReference> pages;
for (const QString& fileName : files)
@ -161,6 +162,7 @@ int PDFToolUnite::execute(const PDFToolOptions& options)
documentBuilder.appendTo(ocPropertiesMerged, documentBuilder.getObjectByReference(ocPropertiesReference));
documentBuilder.appendTo(formMerged, documentBuilder.getObjectByReference(acroFormReference));
documentBuilder.mergeNames(namesMerged, namesReference);
pages.insert(pages.end(), references.cbegin(), references.cend());
}
@ -170,6 +172,11 @@ int PDFToolUnite::execute(const PDFToolOptions& options)
documentBuilder.setCatalogOptionalContentProperties(ocPropertiesMerged);
}
if (!documentBuilder.getObjectByReference(namesMerged).isNull())
{
documentBuilder.setCatalogNames(namesMerged);
}
if (!documentBuilder.getObjectByReference(formMerged).isNull())
{
documentBuilder.setCatalogAcroForm(formMerged);

View File

@ -7859,5 +7859,62 @@ updateDocumentInfo(qMove(updatedInfoDictionary));</property>
<property name="functionDescription">This function is used to update trailer dictionary. Must be called each time the final document is being built.</property>
<property name="returnType">_void</property>
</QObject>
<QObject class="codegen::GeneratedFunction">
<property name="objectName"></property>
<property name="items">
<QObject class="codegen::GeneratedAction">
<property name="objectName"></property>
<property name="items">
<QObject class="codegen::GeneratedParameter">
<property name="objectName"></property>
<property name="items"/>
<property name="parameterName">names</property>
<property name="parameterType">_PDFObjectReference</property>
<property name="parameterDescription">Reference to Names dictionary.</property>
</QObject>
</property>
<property name="actionType">Parameters</property>
<property name="variableName"></property>
<property name="variableType">_void</property>
<property name="code"></property>
</QObject>
<QObject class="codegen::GeneratedAction">
<property name="objectName"></property>
<property name="items">
<QObject class="codegen::GeneratedPDFObject">
<property name="objectName"></property>
<property name="items">
<QObject class="codegen::GeneratedPDFObject">
<property name="objectName"></property>
<property name="items"/>
<property name="dictionaryItemName">Names</property>
<property name="objectType">DictionaryItemSimple</property>
<property name="value">((names.isValid()) ? PDFObject::createReference(names) : PDFObject())</property>
</QObject>
</property>
<property name="dictionaryItemName"></property>
<property name="objectType">Dictionary</property>
<property name="value"></property>
</QObject>
</property>
<property name="actionType">CreateObject</property>
<property name="variableName">updatedCatalog</property>
<property name="variableType">_PDFObject</property>
<property name="code"></property>
</QObject>
<QObject class="codegen::GeneratedAction">
<property name="objectName"></property>
<property name="items"/>
<property name="actionType">Code</property>
<property name="variableName"></property>
<property name="variableType">_void</property>
<property name="code">mergeTo(getCatalogReference(), updatedCatalog);</property>
</QObject>
</property>
<property name="functionType">Structure</property>
<property name="functionName">setCatalogNames</property>
<property name="functionDescription">Set reference to 'Names' dictionary to catalog.</property>
<property name="returnType">_void</property>
</QObject>
</property>
</root>