From 5d5973772ab7a6dd29a30bbb824ae70d9e5226a6 Mon Sep 17 00:00:00 2001 From: Jakub Melka Date: Sat, 31 Oct 2020 17:11:51 +0100 Subject: [PATCH] Unite tool (basics) --- PdfForQtLib/sources/pdfdocument.h | 2 +- PdfForQtLib/sources/pdfdocumentbuilder.cpp | 107 +++++---- PdfForQtLib/sources/pdfdocumentbuilder.h | 84 ++++++-- PdfForQtLib/sources/pdfobject.cpp | 59 +++++ PdfForQtLib/sources/pdfobject.h | 6 +- PdfTool/PdfTool.pro | 2 + PdfTool/pdftoolabstractapplication.cpp | 11 + PdfTool/pdftoolabstractapplication.h | 5 + PdfTool/pdftoolunite.cpp | 210 ++++++++++++++++++ PdfTool/pdftoolunite.h | 36 ++++ generated_code_definition.xml | 239 +++++++++++++-------- 11 files changed, 608 insertions(+), 153 deletions(-) create mode 100644 PdfTool/pdftoolunite.cpp create mode 100644 PdfTool/pdftoolunite.h diff --git a/PdfForQtLib/sources/pdfdocument.h b/PdfForQtLib/sources/pdfdocument.h index 14cc572..c29b578 100644 --- a/PdfForQtLib/sources/pdfdocument.h +++ b/PdfForQtLib/sources/pdfdocument.h @@ -147,7 +147,7 @@ private: /// then if object with valid data is not found, default value is used, and second one, /// without default value, if valid data are not found, then exception is thrown. /// This class uses Decorator design pattern. -class PDFDocumentDataLoaderDecorator +class PDFFORQTLIBSHARED_EXPORT PDFDocumentDataLoaderDecorator { public: explicit PDFDocumentDataLoaderDecorator(const PDFDocument* document); diff --git a/PdfForQtLib/sources/pdfdocumentbuilder.cpp b/PdfForQtLib/sources/pdfdocumentbuilder.cpp index 924ad06..a08b654 100644 --- a/PdfForQtLib/sources/pdfdocumentbuilder.cpp +++ b/PdfForQtLib/sources/pdfdocumentbuilder.cpp @@ -752,6 +752,11 @@ void PDFDocumentBuilder::mergeTo(PDFObjectReference reference, PDFObject object) m_storage.setObject(reference, PDFObjectManipulator::merge(m_storage.getObject(reference), qMove(object), PDFObjectManipulator::RemoveNullObjects)); } +void PDFDocumentBuilder::setObject(PDFObjectReference reference, PDFObject object) +{ + m_storage.setObject(reference, qMove(object)); +} + void PDFDocumentBuilder::appendTo(PDFObjectReference reference, PDFObject object) { m_storage.setObject(reference, PDFObjectManipulator::merge(m_storage.getObject(reference), qMove(object), PDFObjectManipulator::ConcatenateArrays)); @@ -1016,6 +1021,20 @@ std::vector PDFDocumentBuilder::copyFrom(const std::vector return result; } +std::vector PDFDocumentBuilder::createObjectsFromReferences(const std::vector& references) +{ + std::vector result; + std::transform(references.cbegin(), references.cend(), std::back_inserter(result), [](const PDFObjectReference& reference) { return PDFObject::createReference(reference); }); + return result; +} + +std::vector PDFDocumentBuilder::createReferencesFromObjects(const std::vector& objects) +{ + std::vector result; + std::transform(objects.cbegin(), objects.cend(), std::back_inserter(result), [](const PDFObject& object) { Q_ASSERT(object.isReference()); return object.getReference(); }); + return result; +} + const PDFFormManager* PDFDocumentBuilder::getFormManager() const { return m_formManager; @@ -2863,6 +2882,23 @@ PDFObject PDFDocumentBuilder::createTrailerDictionary(PDFObjectReference catalog } +void PDFDocumentBuilder::removeDocumentActions() +{ + PDFObjectFactory objectBuilder; + + objectBuilder.beginDictionary(); + objectBuilder.beginDictionaryItem("OpenAction"); + objectBuilder << PDFObject(); + objectBuilder.endDictionaryItem(); + objectBuilder.beginDictionaryItem("AA"); + objectBuilder << PDFObject(); + objectBuilder.endDictionaryItem(); + objectBuilder.endDictionary(); + PDFObject updatedCatalog = objectBuilder.takeObject(); + mergeTo(getCatalogReference(), updatedCatalog); +} + + void PDFDocumentBuilder::removeOutline() { PDFObjectFactory objectBuilder; @@ -2877,6 +2913,37 @@ void PDFDocumentBuilder::removeOutline() } +void PDFDocumentBuilder::removeStructureTree() +{ + PDFObjectFactory objectBuilder; + + objectBuilder.beginDictionary(); + objectBuilder.beginDictionaryItem("StructTreeRoot"); + objectBuilder << PDFObject(); + objectBuilder.endDictionaryItem(); + objectBuilder.beginDictionaryItem("MarkInfo"); + objectBuilder << PDFObject(); + objectBuilder.endDictionaryItem(); + objectBuilder.endDictionary(); + PDFObject updatedCatalog = objectBuilder.takeObject(); + mergeTo(getCatalogReference(), updatedCatalog); +} + + +void PDFDocumentBuilder::removeThreads() +{ + PDFObjectFactory objectBuilder; + + objectBuilder.beginDictionary(); + objectBuilder.beginDictionaryItem("Threads"); + objectBuilder << PDFObject(); + objectBuilder.endDictionaryItem(); + objectBuilder.endDictionary(); + PDFObject updatedCatalog = objectBuilder.takeObject(); + mergeTo(getCatalogReference(), updatedCatalog); +} + + void PDFDocumentBuilder::setAnnotationAppearanceState(PDFObjectReference annotation, QByteArray appearanceState) { @@ -3245,47 +3312,13 @@ void PDFDocumentBuilder::updateTrailerDictionary(PDFInteger objectCount) } -void PDFDocumentBuilder::removeThreads() +void PDFDocumentBuilder::setCatalogOptionalContentProperties(PDFObjectReference ocProperties) { PDFObjectFactory objectBuilder; objectBuilder.beginDictionary(); - objectBuilder.beginDictionaryItem("Threads"); - objectBuilder << PDFObject(); - objectBuilder.endDictionaryItem(); - objectBuilder.endDictionary(); - PDFObject updatedCatalog = objectBuilder.takeObject(); - mergeTo(getCatalogReference(), updatedCatalog); -} - - -void PDFDocumentBuilder::removeDocumentActions() -{ - PDFObjectFactory objectBuilder; - - objectBuilder.beginDictionary(); - objectBuilder.beginDictionaryItem("OpenAction"); - objectBuilder << PDFObject(); - objectBuilder.endDictionaryItem(); - objectBuilder.beginDictionaryItem("AA"); - objectBuilder << PDFObject(); - objectBuilder.endDictionaryItem(); - objectBuilder.endDictionary(); - PDFObject updatedCatalog = objectBuilder.takeObject(); - mergeTo(getCatalogReference(), updatedCatalog); -} - - -void PDFDocumentBuilder::removeStructureTree() -{ - PDFObjectFactory objectBuilder; - - objectBuilder.beginDictionary(); - objectBuilder.beginDictionaryItem("StructTreeRoot"); - objectBuilder << PDFObject(); - objectBuilder.endDictionaryItem(); - objectBuilder.beginDictionaryItem("MarkInfo"); - objectBuilder << PDFObject(); + objectBuilder.beginDictionaryItem("OCProperties"); + objectBuilder << ocProperties; objectBuilder.endDictionaryItem(); objectBuilder.endDictionary(); PDFObject updatedCatalog = objectBuilder.takeObject(); diff --git a/PdfForQtLib/sources/pdfdocumentbuilder.h b/PdfForQtLib/sources/pdfdocumentbuilder.h index a72e4f6..62759c2 100644 --- a/PdfForQtLib/sources/pdfdocumentbuilder.h +++ b/PdfForQtLib/sources/pdfdocumentbuilder.h @@ -302,6 +302,52 @@ public: /// be flattened to use this function. \sa flattenPageTree std::vector getPages() const; + /// Adds a new objet to the object storage + /// \param object Object + PDFObjectReference addObject(PDFObject object); + + /// Copies objects from another storage. Objects have adjusted references to match + /// this storage references and objects are added after the last objects of active storage. + /// This function can also automatically create references from direct objects passed + /// by parameter \p objects, if \p createReferences is set to true. + /// \param objects Objects, which we want to copy from another storage + /// \param storage Storage, from which we are copying from + /// \param createReferences Create references from \p objects + std::vector copyFrom(const std::vector& objects, const PDFObjectStorage& storage, bool createReferences); + + /// Creates object list from reference list (objects are references) + /// \param references References + static std::vector createObjectsFromReferences(const std::vector& references); + + /// Creates reference list from object list. Object list must be + /// a list of references, it is invalid to pass this function objects, + /// which are not references. + /// \param objects Objects with references + static std::vector createReferencesFromObjects(const std::vector& objects); + + /// Returns catalog reference + PDFObjectReference getCatalogReference() const; + + /// Returns object storage + const PDFObjectStorage* getStorage() const { return &m_storage; } + + /// Appends object to target object. Targed object reference must be valid. + /// Arrays are concatenated. + /// \param reference Target object reference + /// \param object Object to be appended + void appendTo(PDFObjectReference reference, PDFObject object); + + /// Merges object to target object. Targed object reference must be valid. + /// Arrays are not concatenated. + /// \param reference Target object reference + /// \param object Object to be merged + void mergeTo(PDFObjectReference reference, PDFObject object); + + /// Sets object by reference + /// \param reference Target object reference + /// \param object Object to be set + void setObject(PDFObjectReference reference, PDFObject object); + /* START GENERATED CODE */ /// Appends a new page after last page. @@ -823,10 +869,22 @@ public: PDFObject createTrailerDictionary(PDFObjectReference catalog); + /// Removes document actions from document catalog. + void removeDocumentActions(); + + /// Removes outline tree from document catalog. void removeOutline(); + /// Removes structure tree from document catalog. + void removeStructureTree(); + + + /// Removes threads from document catalog. + void removeThreads(); + + /// Sets annotation appearance state. /// \param annotation Annotation /// \param appearanceState Appearance state @@ -988,42 +1046,22 @@ public: void updateTrailerDictionary(PDFInteger objectCount); - /// Removes threads from document catalog. - void removeThreads(); - - - /// Removes document actions from document catalog. - void removeDocumentActions(); - - - /// Removes structure tree from document catalog. - void removeStructureTree(); + /// Set optional content properties to catalog. + /// \param ocProperties Reference to catalog optional content properties. + void setCatalogOptionalContentProperties(PDFObjectReference ocProperties); /* END GENERATED CODE */ private: - PDFObjectReference addObject(PDFObject object); - void mergeTo(PDFObjectReference reference, PDFObject object); - void appendTo(PDFObjectReference reference, PDFObject object); QRectF getPopupWindowRect(const QRectF& rectangle) const; QString getProducerString() const; PDFObjectReference getPageTreeRoot() const; PDFInteger getPageTreeRootChildCount() const; PDFObjectReference getDocumentInfo() const; - PDFObjectReference getCatalogReference() const; void updateDocumentInfo(PDFObject info); QRectF getPolygonsBoundingRect(const Polygons& Polygons) const; - /// Copies objects from another storage. Objects have adjusted references to match - /// this storage references and objects are added after the last objects of active storage. - /// This function can also automatically create references from direct objects passed - /// by parameter \p objects, if \p createReferences is set to true. - /// \param objects Objects, which we want to copy from another storage - /// \param storage Storage, from which we are copying from - /// \param createReferences Create references from \p objects - std::vector copyFrom(const std::vector& objects, const PDFObjectStorage& storage, bool createReferences); - PDFObjectStorage m_storage; PDFVersion m_version; const PDFFormManager* m_formManager = nullptr; diff --git a/PdfForQtLib/sources/pdfobject.cpp b/PdfForQtLib/sources/pdfobject.cpp index 2567b9b..5181f45 100644 --- a/PdfForQtLib/sources/pdfobject.cpp +++ b/PdfForQtLib/sources/pdfobject.cpp @@ -427,6 +427,65 @@ PDFObject PDFObjectManipulator::removeNullObjects(PDFObject object) return merge(object, object, RemoveNullObjects); } +PDFObject PDFObjectManipulator::removeDuplicitReferencesInArrays(PDFObject object) +{ + switch (object.getType()) + { + case PDFObject::Type::Stream: + { + const PDFStream* stream = object.getStream(); + PDFDictionary dictionary = *stream->getDictionary(); + + for (size_t i = 0, count = dictionary.getCount(); i < count; ++i) + { + dictionary.setEntry(dictionary.getKey(i), removeDuplicitReferencesInArrays(dictionary.getValue(i))); + } + + return PDFObject::createStream(std::make_shared(qMove(dictionary), QByteArray(*stream->getContent()))); + } + + case PDFObject::Type::Dictionary: + { + PDFDictionary dictionary = *object.getDictionary(); + + for (size_t i = 0, count = dictionary.getCount(); i < count; ++i) + { + dictionary.setEntry(dictionary.getKey(i), removeDuplicitReferencesInArrays(dictionary.getValue(i))); + } + + return PDFObject::createDictionary(std::make_shared(qMove(dictionary))); + } + + case PDFObject::Type::Array: + { + PDFArray array; + std::set usedReferences; + + for (const PDFObject& object : *object.getArray()) + { + if (object.isReference()) + { + PDFObjectReference reference = object.getReference(); + if (!usedReferences.count(reference)) + { + usedReferences.insert(reference); + array.appendItem(PDFObject::createReference(reference)); + } + } + else + { + array.appendItem(removeDuplicitReferencesInArrays(object)); + } + } + + return PDFObject::createArray(std::make_shared(qMove(array))); + } + + default: + return object; + } +} + QByteArray PDFStringRef::getString() const { if (inplaceString) diff --git a/PdfForQtLib/sources/pdfobject.h b/PdfForQtLib/sources/pdfobject.h index 4225cff..455d450 100644 --- a/PdfForQtLib/sources/pdfobject.h +++ b/PdfForQtLib/sources/pdfobject.h @@ -476,7 +476,7 @@ private: QByteArray m_content; }; -class PDFObjectManipulator +class PDFFORQTLIBSHARED_EXPORT PDFObjectManipulator { public: explicit PDFObjectManipulator() = delete; @@ -503,6 +503,10 @@ public: /// Remove null objects from all dictionaries /// \param object Object static PDFObject removeNullObjects(PDFObject object); + + /// Remove duplicit references from arrays + /// \param object Object + static PDFObject removeDuplicitReferencesInArrays(PDFObject object); }; } // namespace pdf diff --git a/PdfTool/PdfTool.pro b/PdfTool/PdfTool.pro index 002be2e..90f6f29 100644 --- a/PdfTool/PdfTool.pro +++ b/PdfTool/PdfTool.pro @@ -56,6 +56,7 @@ SOURCES += \ pdftoolinfostructuretree.cpp \ pdftoolrender.cpp \ pdftoolseparate.cpp \ + pdftoolunite.cpp \ pdftoolverifysignatures.cpp \ pdftoolxml.cpp @@ -85,5 +86,6 @@ HEADERS += \ pdftoolinfostructuretree.h \ pdftoolrender.h \ pdftoolseparate.h \ + pdftoolunite.h \ pdftoolverifysignatures.h \ pdftoolxml.h diff --git a/PdfTool/pdftoolabstractapplication.cpp b/PdfTool/pdftoolabstractapplication.cpp index cb88864..6ab7e83 100644 --- a/PdfTool/pdftoolabstractapplication.cpp +++ b/PdfTool/pdftoolabstractapplication.cpp @@ -169,6 +169,12 @@ void PDFToolAbstractApplication::initializeCommandLineParser(QCommandLineParser* parser->addPositionalArgument("pattern", "Page pattern, must contain '%' character if multiple pages are selected."); } + if (optionFlags.testFlag(Unite)) + { + parser->addPositionalArgument("source", "Documents to be merged into single document.", "file1.pdf [file2.pdf, ...]"); + parser->addPositionalArgument("target", "Merged document filename."); + } + if (optionFlags.testFlag(SignatureVerification)) { parser->addOption(QCommandLineOption("ver-no-user-cert", "Disable user certificate store.")); @@ -809,6 +815,11 @@ PDFToolOptions PDFToolAbstractApplication::getOptions(QCommandLineParser* parser options.renderShowPageStatistics = parser->isSet("render-show-page-stat"); } + if (optionFlags.testFlag(Unite)) + { + options.uniteFiles = positionalArguments; + } + return options; } diff --git a/PdfTool/pdftoolabstractapplication.h b/PdfTool/pdftoolabstractapplication.h index 516eb39..c3c2220 100644 --- a/PdfTool/pdftoolabstractapplication.h +++ b/PdfTool/pdftoolabstractapplication.h @@ -132,6 +132,9 @@ struct PDFToolOptions // For option 'Separate' QString separatePagePattern; + // For option 'Unite' + QStringList uniteFiles; + /// Returns page range. If page range is invalid, then \p errorMessage is empty. /// \param pageCount Page count /// \param[out] errorMessage Error message @@ -160,6 +163,7 @@ public: { ExitSuccess = EXIT_SUCCESS, ExitFailure = EXIT_FAILURE, + ErrorUnknown, ErrorNoDocumentSpecified, ErrorDocumentReading, ErrorInvalidArguments, @@ -198,6 +202,7 @@ public: ColorManagementSystem = 0x00010000, ///< Color management system settings RenderFlags = 0x00020000, ///< Render flags for page image rasterizer Separate = 0x00040000, ///< Settings for Separate tool + Unite = 0x00080000, ///< Settings for Unite tool }; Q_DECLARE_FLAGS(Options, Option) diff --git a/PdfTool/pdftoolunite.cpp b/PdfTool/pdftoolunite.cpp new file mode 100644 index 0000000..4b80f0a --- /dev/null +++ b/PdfTool/pdftoolunite.cpp @@ -0,0 +1,210 @@ +// Copyright (C) 2020 Jakub Melka +// +// This file is part of PdfForQt. +// +// PdfForQt is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// PdfForQt is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with PDFForQt. If not, see . + +#include "pdftoolunite.h" +#include "pdfdocumentbuilder.h" +#include "pdfdocumentreader.h" +#include "pdfoptimizer.h" +#include "pdfdocumentwriter.h" + +namespace pdftool +{ + +static PDFToolUnite s_toolUniteApplication; + +QString PDFToolUnite::getStandardString(PDFToolAbstractApplication::StandardString standardString) const +{ + switch (standardString) + { + case Command: + return "unite"; + + case Name: + return PDFToolTranslationContext::tr("Merge documents"); + + case Description: + return PDFToolTranslationContext::tr("Merge multiple documents to a single document."); + + default: + Q_ASSERT(false); + break; + } + + return QString(); +} + +int PDFToolUnite::execute(const PDFToolOptions& options) +{ + if (options.uniteFiles.size() < 3) + { + PDFConsole::writeError(PDFToolTranslationContext::tr("At least two documents and target (merged) document must be specified."), options.outputCodec); + return ErrorInvalidArguments; + } + + QStringList files = options.uniteFiles; + QString targetFile = files.back(); + files.pop_back(); + + if (QFileInfo::exists(targetFile)) + { + PDFConsole::writeError(PDFToolTranslationContext::tr("Target file '%1' already exists. Document merging not performed.").arg(targetFile), options.outputCodec); + return ErrorFailedWriteToFile; + } + + try + { + pdf::PDFDocumentBuilder documentBuilder; + documentBuilder.createDocument(); + + pdf::PDFObjectReference ocPropertiesMerged = documentBuilder.addObject(pdf::PDFObject()); + + std::vector pages; + for (const QString& fileName : files) + { + pdf::PDFDocumentReader reader(nullptr, [](bool* ok) { *ok = false; return QString(); }, options.permissiveReading); + pdf::PDFDocument document = reader.readFromFile(fileName); + if (reader.getReadingResult() != pdf::PDFDocumentReader::Result::OK) + { + PDFConsole::writeError(PDFToolTranslationContext::tr("Cannot open document '%1'.").arg(fileName), options.outputCodec); + return ErrorDocumentReading; + } + + if (!document.getStorage().getSecurityHandler()->isAllowed(pdf::PDFSecurityHandler::Permission::CopyContent)) + { + PDFConsole::writeError(PDFToolTranslationContext::tr("Document doesn't allow to copy content."), options.outputCodec); + return ErrorPermissions; + } + + pdf::PDFDocumentBuilder temporaryBuilder(&document); + temporaryBuilder.flattenPageTree(); + + std::vector currentPages = temporaryBuilder.getPages(); + std::vector objectsToMerge = currentPages; + + pdf::PDFObjectReference acroFormReference; + pdf::PDFObjectReference namesReference; + pdf::PDFObjectReference ocPropertiesReference; + + pdf::PDFObject formObject = document.getCatalog()->getFormObject(); + if (formObject.isReference()) + { + acroFormReference = formObject.getReference(); + } + else + { + acroFormReference = temporaryBuilder.addObject(formObject); + } + + if (const pdf::PDFDictionary* catalogDictionary = temporaryBuilder.getDictionaryFromObject(temporaryBuilder.getObjectByReference(temporaryBuilder.getCatalogReference()))) + { + pdf::PDFObject namesObject = catalogDictionary->get("Names"); + if (namesObject.isReference()) + { + namesReference = namesObject.getReference(); + } + else + { + namesReference = temporaryBuilder.addObject(namesObject); + } + + pdf::PDFObject ocPropertiesObject = catalogDictionary->get("OCProperties"); + if (ocPropertiesObject.isReference()) + { + ocPropertiesReference = ocPropertiesObject.getReference(); + } + else + { + ocPropertiesReference = temporaryBuilder.addObject(ocPropertiesObject); + } + } + + if (!namesReference.isValid()) + { + namesReference = temporaryBuilder.addObject(pdf::PDFObject()); + } + + if (!ocPropertiesReference.isValid()) + { + ocPropertiesReference = temporaryBuilder.addObject(pdf::PDFObject()); + } + + objectsToMerge.insert(objectsToMerge.end(), { acroFormReference, namesReference, ocPropertiesReference }); + + // Now, we are ready to merge objects into targed document builder + std::vector references = pdf::PDFDocumentBuilder::createReferencesFromObjects(documentBuilder.copyFrom(pdf::PDFDocumentBuilder::createObjectsFromReferences(objectsToMerge), *temporaryBuilder.getStorage(), true)); + + ocPropertiesReference = references.back(); + references.pop_back(); + namesReference = references.back(); + references.pop_back(); + acroFormReference = references.back(); + references.pop_back(); + + documentBuilder.appendTo(ocPropertiesMerged, documentBuilder.getObjectByReference(ocPropertiesReference)); + pages.insert(pages.end(), references.cbegin(), references.cend()); + } + + documentBuilder.setPages(pages); + if (!documentBuilder.getObjectByReference(ocPropertiesMerged).isNull()) + { + documentBuilder.setCatalogOptionalContentProperties(ocPropertiesMerged); + } + + pdf::PDFDocument mergedDocument = documentBuilder.build(); + + // Optimize document - remove unused objects and shrink object storage + pdf::PDFOptimizer optimizer(pdf::PDFOptimizer::RemoveUnusedObjects | pdf::PDFOptimizer::ShrinkObjectStorage | pdf::PDFOptimizer::DereferenceSimpleObjects | pdf::PDFOptimizer::MergeIdenticalObjects, nullptr); + optimizer.setDocument(&mergedDocument); + optimizer.optimize(); + mergedDocument = optimizer.takeOptimizedDocument(); + + // We must adjust some objects - they can be merged + pdf::PDFDocumentBuilder finalBuilder(&mergedDocument); + if (const pdf::PDFDictionary* dictionary = finalBuilder.getDictionaryFromObject(finalBuilder.getObjectByReference(finalBuilder.getCatalogReference()))) + { + pdf::PDFDocumentDataLoaderDecorator loader(finalBuilder.getStorage()); + pdf::PDFObjectReference ocPropertiesReference = loader.readReferenceFromDictionary(dictionary, "OCProperties"); + if (ocPropertiesReference.isValid()) + { + finalBuilder.setObject(ocPropertiesReference, pdf::PDFObjectManipulator::removeDuplicitReferencesInArrays(finalBuilder.getObjectByReference(ocPropertiesReference))); + } + } + mergedDocument = finalBuilder.build(); + + pdf::PDFDocumentWriter writer(nullptr); + pdf::PDFOperationResult result = writer.write(targetFile, &mergedDocument, false); + if (!result) + { + PDFConsole::writeError(result.getErrorMessage(), options.outputCodec); + return ErrorFailedWriteToFile; + } + } + catch (pdf::PDFException exception) + { + PDFConsole::writeError(exception.getMessage(), options.outputCodec); + return ErrorUnknown; + } + + return ExitSuccess; +} + +PDFToolAbstractApplication::Options PDFToolUnite::getOptionsFlags() const +{ + return ConsoleFormat | Unite; +} + +} // namespace pdftool diff --git a/PdfTool/pdftoolunite.h b/PdfTool/pdftoolunite.h new file mode 100644 index 0000000..63feaa3 --- /dev/null +++ b/PdfTool/pdftoolunite.h @@ -0,0 +1,36 @@ +// Copyright (C) 2020 Jakub Melka +// +// This file is part of PdfForQt. +// +// PdfForQt is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// PdfForQt is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with PDFForQt. If not, see . + +#ifndef PDFTOOLUNITE_H +#define PDFTOOLUNITE_H + +#include "pdftoolabstractapplication.h" + +namespace pdftool +{ + +class PDFToolUnite : public PDFToolAbstractApplication +{ +public: + virtual QString getStandardString(StandardString standardString) const override; + virtual int execute(const PDFToolOptions& options) override; + virtual Options getOptionsFlags() const override; +}; + +} // namespace pdftool + +#endif // PDFTOOLUNITE_H diff --git a/generated_code_definition.xml b/generated_code_definition.xml index 708b342..22dcd27 100644 --- a/generated_code_definition.xml +++ b/generated_code_definition.xml @@ -5763,6 +5763,54 @@ return annotationObject; This function is used to create a new trailer dictionary, when blank document is created. Do not call this function manually. _PDFObject + + + + + + + + + + + + + OpenAction + DictionaryItemSimple + PDFObject() + + + + + AA + DictionaryItemSimple + PDFObject() + + + + Dictionary + + + + CreateObject + updatedCatalog + _PDFObject + + + + + + Code + + _void + mergeTo(getCatalogReference(), updatedCatalog); + + + Structure + removeDocumentActions + Removes document actions from document catalog. + _void + @@ -5804,6 +5852,95 @@ return annotationObject; Removes outline tree from document catalog. _void + + + + + + + + + + + + + StructTreeRoot + DictionaryItemSimple + PDFObject() + + + + + MarkInfo + DictionaryItemSimple + PDFObject() + + + + Dictionary + + + + CreateObject + updatedCatalog + _PDFObject + + + + + + Code + + _void + mergeTo(getCatalogReference(), updatedCatalog); + + + Structure + removeStructureTree + Removes structure tree from document catalog. + _void + + + + + + + + + + + + + + Threads + DictionaryItemSimple + PDFObject() + + + + Dictionary + + + + CreateObject + updatedCatalog + _PDFObject + + + + + + Code + + _void + mergeTo(getCatalogReference(), updatedCatalog); + + + Structure + removeThreads + Removes threads from document catalog. + _void + @@ -7336,44 +7473,19 @@ updateDocumentInfo(qMove(updatedInfoDictionary)); - + - - - - - Threads - DictionaryItemSimple - PDFObject() - - - - Dictionary - + + ocProperties + _PDFObjectReference + Reference to catalog optional content properties. - CreateObject - updatedCatalog - _PDFObject - - - - - - Code + Parameters _void - mergeTo(getCatalogReference(), updatedCatalog); + - - Structure - removeThreads - Removes threads from document catalog. - _void - - - - @@ -7383,16 +7495,9 @@ updateDocumentInfo(qMove(updatedInfoDictionary)); - OpenAction + OCProperties DictionaryItemSimple - PDFObject() - - - - - AA - DictionaryItemSimple - PDFObject() + ocProperties @@ -7415,56 +7520,8 @@ updateDocumentInfo(qMove(updatedInfoDictionary)); Structure - removeDocumentActions - Removes document actions from document catalog. - _void - - - - - - - - - - - - - - StructTreeRoot - DictionaryItemSimple - PDFObject() - - - - - MarkInfo - DictionaryItemSimple - PDFObject() - - - - Dictionary - - - - CreateObject - updatedCatalog - _PDFObject - - - - - - Code - - _void - mergeTo(getCatalogReference(), updatedCatalog); - - - Structure - removeStructureTree - Removes structure tree from document catalog. + setCatalogOptionalContentProperties + Set optional content properties to catalog. _void