diff --git a/PdfForQtLib/sources/pdfdocumentbuilder.cpp b/PdfForQtLib/sources/pdfdocumentbuilder.cpp index c0106ba..7f099b6 100644 --- a/PdfForQtLib/sources/pdfdocumentbuilder.cpp +++ b/PdfForQtLib/sources/pdfdocumentbuilder.cpp @@ -20,6 +20,7 @@ #include "pdfconstants.h" #include "pdfdocumentreader.h" #include "pdfobjectutils.h" +#include "pdfnametreeloader.h" #include #include @@ -798,6 +799,80 @@ void PDFDocumentBuilder::createDocumentParts(const std::vector& 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 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::parse(&m_storage, aDict->get(key), getObject); + auto bMap = PDFNameTreeLoader::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 diff --git a/PdfForQtLib/sources/pdfdocumentbuilder.h b/PdfForQtLib/sources/pdfdocumentbuilder.h index 599dcdb..dd80d59 100644 --- a/PdfForQtLib/sources/pdfdocumentbuilder.h +++ b/PdfForQtLib/sources/pdfdocumentbuilder.h @@ -354,6 +354,12 @@ public: /// \param parts Parts (page count of each document part) void createDocumentParts(const std::vector& 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: diff --git a/PdfTool/pdftoolunite.cpp b/PdfTool/pdftoolunite.cpp index edaa964..b097658 100644 --- a/PdfTool/pdftoolunite.cpp +++ b/PdfTool/pdftoolunite.cpp @@ -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 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); diff --git a/generated_code_definition.xml b/generated_code_definition.xml index c4fa34e..d6e56a6 100644 --- a/generated_code_definition.xml +++ b/generated_code_definition.xml @@ -7859,5 +7859,62 @@ updateDocumentInfo(qMove(updatedInfoDictionary)); This function is used to update trailer dictionary. Must be called each time the final document is being built. _void + + + + + + + + + + names + _PDFObjectReference + Reference to Names dictionary. + + + Parameters + + _void + + + + + + + + + + + + Names + DictionaryItemSimple + ((names.isValid()) ? PDFObject::createReference(names) : PDFObject()) + + + + Dictionary + + + + CreateObject + updatedCatalog + _PDFObject + + + + + + Code + + _void + mergeTo(getCatalogReference(), updatedCatalog); + + + Structure + setCatalogNames + Set reference to 'Names' dictionary to catalog. + _void +