diff --git a/PdfForQtLib/sources/pdfdocumentbuilder.cpp b/PdfForQtLib/sources/pdfdocumentbuilder.cpp index a08b654..c0106ba 100644 --- a/PdfForQtLib/sources/pdfdocumentbuilder.cpp +++ b/PdfForQtLib/sources/pdfdocumentbuilder.cpp @@ -757,6 +757,47 @@ void PDFDocumentBuilder::setObject(PDFObjectReference reference, PDFObject objec m_storage.setObject(reference, qMove(object)); } +void PDFDocumentBuilder::createDocumentParts(const std::vector& parts) +{ + PDFObjectReference root = createDocumentPartRoot(); + std::vector pages = getPages(); + + PDFObjectFactory objectFactory; + objectFactory.beginDictionary(); + objectFactory.beginDictionaryItem("DParts"); + objectFactory.beginArray(); + + size_t start = 0; + for (std::size_t count : parts) + { + if (count == 0) + { + continue; + } + + auto itStart = std::next(pages.cbegin(), start); + auto itEnd = std::next(itStart, count); + + PDFObjectReference item = createDocumentPartItem(*itStart, *std::prev(itEnd), root); + for (auto it = itStart; it != itEnd; ++it) + { + setPageDocumentPart(*it, item); + } + + objectFactory.beginArray(); + objectFactory << item; + objectFactory.endArray(); + + start += count; + } + + objectFactory.endArray(); + objectFactory.endDictionaryItem(); + objectFactory.endDictionary(); + + mergeTo(root, objectFactory.takeObject()); +} + void PDFDocumentBuilder::appendTo(PDFObjectReference reference, PDFObject object) { m_storage.setObject(reference, PDFObjectManipulator::merge(m_storage.getObject(reference), qMove(object), PDFObjectManipulator::ConcatenateArrays)); @@ -2850,6 +2891,68 @@ PDFObjectReference PDFDocumentBuilder::createCatalogPageTreeRoot() } +PDFObjectReference PDFDocumentBuilder::createDocumentPartItem(PDFObjectReference startPage, + PDFObjectReference endPage, + PDFObjectReference parent) +{ + PDFObjectFactory objectBuilder; + + objectBuilder.beginDictionary(); + objectBuilder.beginDictionaryItem("Type"); + objectBuilder << WrapName("DPart"); + objectBuilder.endDictionaryItem(); + objectBuilder.beginDictionaryItem("Parent"); + objectBuilder << parent; + objectBuilder.endDictionaryItem(); + objectBuilder.beginDictionaryItem("Start"); + objectBuilder << startPage; + objectBuilder.endDictionaryItem(); + objectBuilder.beginDictionaryItem("End"); + objectBuilder << endPage; + objectBuilder.endDictionaryItem(); + objectBuilder.endDictionary(); + PDFObjectReference documentPart = addObject(objectBuilder.takeObject()); + return documentPart; +} + + +PDFObjectReference PDFDocumentBuilder::createDocumentPartRoot() +{ + PDFObjectFactory objectBuilder; + + objectBuilder.beginDictionary(); + objectBuilder.beginDictionaryItem("Type"); + objectBuilder << WrapName("DPart"); + objectBuilder.endDictionaryItem(); + objectBuilder.endDictionary(); + PDFObjectReference rootNodeReference = addObject(objectBuilder.takeObject()); + objectBuilder.beginDictionary(); + objectBuilder.beginDictionaryItem("Type"); + objectBuilder << WrapName("DPartRoot"); + objectBuilder.endDictionaryItem(); + objectBuilder.beginDictionaryItem("DPartRootNode"); + objectBuilder << rootNodeReference; + objectBuilder.endDictionaryItem(); + objectBuilder.endDictionary(); + PDFObjectReference documentPartReference = addObject(objectBuilder.takeObject()); + objectBuilder.beginDictionary(); + objectBuilder.beginDictionaryItem("Parent"); + objectBuilder << documentPartReference; + objectBuilder.endDictionaryItem(); + objectBuilder.endDictionary(); + PDFObject updatedRootNode = objectBuilder.takeObject(); + objectBuilder.beginDictionary(); + objectBuilder.beginDictionaryItem("DPartRoot"); + objectBuilder << documentPartReference; + objectBuilder.endDictionaryItem(); + objectBuilder.endDictionary(); + PDFObject updatedCatalog = objectBuilder.takeObject(); + mergeTo(rootNodeReference, updatedRootNode); + mergeTo(getCatalogReference(), updatedCatalog); + return rootNodeReference; +} + + PDFObject PDFDocumentBuilder::createTrailerDictionary(PDFObjectReference catalog) { PDFObjectFactory objectBuilder; @@ -3123,6 +3226,34 @@ void PDFDocumentBuilder::setAnnotationTitle(PDFObjectReference annotation, } +void PDFDocumentBuilder::setCatalogAcroForm(PDFObjectReference acroForm) +{ + PDFObjectFactory objectBuilder; + + objectBuilder.beginDictionary(); + objectBuilder.beginDictionaryItem("AcroForm"); + objectBuilder << acroForm; + objectBuilder.endDictionaryItem(); + objectBuilder.endDictionary(); + PDFObject updatedCatalog = objectBuilder.takeObject(); + mergeTo(getCatalogReference(), updatedCatalog); +} + + +void PDFDocumentBuilder::setCatalogOptionalContentProperties(PDFObjectReference ocProperties) +{ + PDFObjectFactory objectBuilder; + + objectBuilder.beginDictionary(); + objectBuilder.beginDictionaryItem("OCProperties"); + objectBuilder << ocProperties; + objectBuilder.endDictionaryItem(); + objectBuilder.endDictionary(); + PDFObject updatedCatalog = objectBuilder.takeObject(); + mergeTo(getCatalogReference(), updatedCatalog); +} + + void PDFDocumentBuilder::setDocumentAuthor(QString author) { PDFObjectFactory objectBuilder; @@ -3288,6 +3419,21 @@ void PDFDocumentBuilder::setLanguage(QString language) } +void PDFDocumentBuilder::setPageDocumentPart(PDFObjectReference page, + PDFObjectReference documentPart) +{ + PDFObjectFactory objectBuilder; + + objectBuilder.beginDictionary(); + objectBuilder.beginDictionaryItem("DPart"); + objectBuilder << documentPart; + objectBuilder.endDictionaryItem(); + objectBuilder.endDictionary(); + PDFObject updatedPage = objectBuilder.takeObject(); + mergeTo(page, updatedPage); +} + + void PDFDocumentBuilder::updateTrailerDictionary(PDFInteger objectCount) { PDFObjectFactory objectBuilder; @@ -3312,20 +3458,6 @@ void PDFDocumentBuilder::updateTrailerDictionary(PDFInteger objectCount) } -void PDFDocumentBuilder::setCatalogOptionalContentProperties(PDFObjectReference ocProperties) -{ - PDFObjectFactory objectBuilder; - - objectBuilder.beginDictionary(); - objectBuilder.beginDictionaryItem("OCProperties"); - objectBuilder << ocProperties; - 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 62759c2..599dcdb 100644 --- a/PdfForQtLib/sources/pdfdocumentbuilder.h +++ b/PdfForQtLib/sources/pdfdocumentbuilder.h @@ -348,6 +348,12 @@ public: /// \param object Object to be set void setObject(PDFObjectReference reference, PDFObject object); + /// Creates document parts from given pages. Pages must be flattened + /// by function \p flattenPageTree. \sa flattenPageTree. Each document + /// part has certain page size, sum of \p parts must equal to page count. + /// \param parts Parts (page count of each document part) + void createDocumentParts(const std::vector& parts); + /* START GENERATED CODE */ /// Appends a new page after last page. @@ -863,6 +869,19 @@ public: PDFObjectReference createCatalogPageTreeRoot(); + /// Creates document part item (for certain range of pages). + /// \param startPage First page of page range. + /// \param endPage Last page of page range. + /// \param parent Parent node (must be a reference to parent node). + PDFObjectReference createDocumentPartItem(PDFObjectReference startPage, + PDFObjectReference endPage, + PDFObjectReference parent); + + + /// Creates document part root node (and setups catalog object). + PDFObjectReference createDocumentPartRoot(); + + /// This function is used to create a new trailer dictionary, when blank document is created. Do not /// call this function manually. /// \param catalog Reference to document catalog @@ -970,6 +989,16 @@ public: QString title); + /// Set AcroForm to catalog. + /// \param acroForm Reference to AcroForm object. + void setCatalogAcroForm(PDFObjectReference acroForm); + + + /// Set optional content properties to catalog. + /// \param ocProperties Reference to catalog optional content properties. + void setCatalogOptionalContentProperties(PDFObjectReference ocProperties); + + /// Set document author. /// \param author Author void setDocumentAuthor(QString author); @@ -1040,17 +1069,19 @@ public: void setLanguage(QString language); + /// Sets document part to page. + /// \param page Page + /// \param documentPart Document part + void setPageDocumentPart(PDFObjectReference page, + PDFObjectReference documentPart); + + /// This function is used to update trailer dictionary. Must be called each time the final document is /// being built. /// \param objectCount Number of objects (including empty ones) void updateTrailerDictionary(PDFInteger objectCount); - /// Set optional content properties to catalog. - /// \param ocProperties Reference to catalog optional content properties. - void setCatalogOptionalContentProperties(PDFObjectReference ocProperties); - - /* END GENERATED CODE */ private: diff --git a/PdfTool/pdftoolunite.cpp b/PdfTool/pdftoolunite.cpp index 4b80f0a..edaa964 100644 --- a/PdfTool/pdftoolunite.cpp +++ b/PdfTool/pdftoolunite.cpp @@ -70,7 +70,10 @@ int PDFToolUnite::execute(const PDFToolOptions& options) pdf::PDFDocumentBuilder documentBuilder; documentBuilder.createDocument(); + std::vector documentPartPageCounts; + pdf::PDFObjectReference ocPropertiesMerged = documentBuilder.addObject(pdf::PDFObject()); + pdf::PDFObjectReference formMerged = documentBuilder.addObject(pdf::PDFObject()); std::vector pages; for (const QString& fileName : files) @@ -154,7 +157,10 @@ int PDFToolUnite::execute(const PDFToolOptions& options) acroFormReference = references.back(); references.pop_back(); + documentPartPageCounts.push_back(references.size()); + documentBuilder.appendTo(ocPropertiesMerged, documentBuilder.getObjectByReference(ocPropertiesReference)); + documentBuilder.appendTo(formMerged, documentBuilder.getObjectByReference(acroFormReference)); pages.insert(pages.end(), references.cbegin(), references.cend()); } @@ -164,6 +170,14 @@ int PDFToolUnite::execute(const PDFToolOptions& options) documentBuilder.setCatalogOptionalContentProperties(ocPropertiesMerged); } + if (!documentBuilder.getObjectByReference(formMerged).isNull()) + { + documentBuilder.setCatalogAcroForm(formMerged); + } + + // Correct page tree (invalid parents are present) + documentBuilder.flattenPageTree(); + documentBuilder.createDocumentParts(documentPartPageCounts); pdf::PDFDocument mergedDocument = documentBuilder.build(); // Optimize document - remove unused objects and shrink object storage @@ -182,6 +196,11 @@ int PDFToolUnite::execute(const PDFToolOptions& options) { finalBuilder.setObject(ocPropertiesReference, pdf::PDFObjectManipulator::removeDuplicitReferencesInArrays(finalBuilder.getObjectByReference(ocPropertiesReference))); } + pdf::PDFObjectReference acroFormReference = loader.readReferenceFromDictionary(dictionary, "AcroForm"); + if (acroFormReference.isValid()) + { + finalBuilder.setObject(acroFormReference, pdf::PDFObjectManipulator::removeDuplicitReferencesInArrays(finalBuilder.getObjectByReference(acroFormReference))); + } } mergedDocument = finalBuilder.build(); diff --git a/generated_code_definition.xml b/generated_code_definition.xml index 22dcd27..c4fa34e 100644 --- a/generated_code_definition.xml +++ b/generated_code_definition.xml @@ -5654,6 +5654,220 @@ return annotationObject; Creates page tree root for the catalog. This function is only called when new document is being created. Do not call this function manually. _PDFObjectReference + + + + + + + + + + startPage + _PDFObjectReference + First page of page range. + + + + + endPage + _PDFObjectReference + Last page of page range. + + + + + parent + _PDFObjectReference + Parent node (must be a reference to parent node). + + + Parameters + + _void + + + + + + + + + + + + Type + DictionaryItemSimple + WrapName("DPart") + + + + + Parent + DictionaryItemSimple + parent + + + + + Start + DictionaryItemSimple + startPage + + + + + End + DictionaryItemSimple + endPage + + + + Dictionary + + + + CreateObject + documentPart + _PDFObjectReference + + + + + + Code + + _void + return documentPart; + + + Structure + createDocumentPartItem + Creates document part item (for certain range of pages). + _PDFObjectReference + + + + + + + + + + + + + + Type + DictionaryItemSimple + WrapName("DPart") + + + + Dictionary + + + + CreateObject + rootNodeReference + _PDFObjectReference + + + + + + + + + + + + Type + DictionaryItemSimple + WrapName("DPartRoot") + + + + + DPartRootNode + DictionaryItemSimple + rootNodeReference + + + + Dictionary + + + + CreateObject + documentPartReference + _PDFObjectReference + + + + + + + + + + + + Parent + DictionaryItemSimple + documentPartReference + + + + Dictionary + + + + CreateObject + updatedRootNode + _PDFObject + + + + + + + + + + + + DPartRoot + DictionaryItemSimple + documentPartReference + + + + Dictionary + + + + CreateObject + updatedCatalog + _PDFObject + + + + + + Code + + _void + mergeTo(rootNodeReference, updatedRootNode); +mergeTo(getCatalogReference(), updatedCatalog); +return rootNodeReference; + + + Structure + createDocumentPartRoot + Creates document part root node (and setups catalog object). + _PDFObjectReference + @@ -6697,6 +6911,120 @@ return annotationObject; Sets annotation title. _void + + + + + + + + + + acroForm + _PDFObjectReference + Reference to AcroForm object. + + + Parameters + + _void + + + + + + + + + + + + AcroForm + DictionaryItemSimple + acroForm + + + + Dictionary + + + + CreateObject + updatedCatalog + _PDFObject + + + + + + Code + + _void + mergeTo(getCatalogReference(), updatedCatalog); + + + Structure + setCatalogAcroForm + Set AcroForm to catalog. + _void + + + + + + + + + + + ocProperties + _PDFObjectReference + Reference to catalog optional content properties. + + + Parameters + + _void + + + + + + + + + + + + OCProperties + DictionaryItemSimple + ocProperties + + + + Dictionary + + + + CreateObject + updatedCatalog + _PDFObject + + + + + + Code + + _void + mergeTo(getCatalogReference(), updatedCatalog); + + + Structure + setCatalogOptionalContentProperties + Set optional content properties to catalog. + _void + @@ -7378,6 +7706,70 @@ return annotationObject; Set document language. _void + + + + + + + + + + page + _PDFObjectReference + Page + + + + + documentPart + _PDFObjectReference + Document part + + + Parameters + + _void + + + + + + + + + + + + DPart + DictionaryItemSimple + documentPart + + + + Dictionary + + + + CreateObject + updatedPage + _PDFObject + + + + + + Code + + _void + mergeTo(page, updatedPage); + + + Structure + setPageDocumentPart + Sets document part to page. + _void + @@ -7467,62 +7859,5 @@ updateDocumentInfo(qMove(updatedInfoDictionary)); This function is used to update trailer dictionary. Must be called each time the final document is being built. _void - - - - - - - - - - ocProperties - _PDFObjectReference - Reference to catalog optional content properties. - - - Parameters - - _void - - - - - - - - - - - - OCProperties - DictionaryItemSimple - ocProperties - - - - Dictionary - - - - CreateObject - updatedCatalog - _PDFObject - - - - - - Code - - _void - mergeTo(getCatalogReference(), updatedCatalog); - - - Structure - setCatalogOptionalContentProperties - Set optional content properties to catalog. - _void -