Unite tool (basics)

This commit is contained in:
Jakub Melka 2020-10-31 17:11:51 +01:00
parent c58158e3ee
commit 5d5973772a
11 changed files with 608 additions and 153 deletions

View File

@ -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);

View File

@ -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<PDFObject> PDFDocumentBuilder::copyFrom(const std::vector<PDFObject>
return result;
}
std::vector<PDFObject> PDFDocumentBuilder::createObjectsFromReferences(const std::vector<PDFObjectReference>& references)
{
std::vector<PDFObject> result;
std::transform(references.cbegin(), references.cend(), std::back_inserter(result), [](const PDFObjectReference& reference) { return PDFObject::createReference(reference); });
return result;
}
std::vector<PDFObjectReference> PDFDocumentBuilder::createReferencesFromObjects(const std::vector<PDFObject>& objects)
{
std::vector<PDFObjectReference> 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();

View File

@ -302,6 +302,52 @@ public:
/// be flattened to use this function. \sa flattenPageTree
std::vector<PDFObjectReference> 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<PDFObject> copyFrom(const std::vector<PDFObject>& objects, const PDFObjectStorage& storage, bool createReferences);
/// Creates object list from reference list (objects are references)
/// \param references References
static std::vector<PDFObject> createObjectsFromReferences(const std::vector<PDFObjectReference>& 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<PDFObjectReference> createReferencesFromObjects(const std::vector<PDFObject>& 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<PDFObject> copyFrom(const std::vector<PDFObject>& objects, const PDFObjectStorage& storage, bool createReferences);
PDFObjectStorage m_storage;
PDFVersion m_version;
const PDFFormManager* m_formManager = nullptr;

View File

@ -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<PDFStream>(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<PDFDictionary>(qMove(dictionary)));
}
case PDFObject::Type::Array:
{
PDFArray array;
std::set<PDFObjectReference> 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<PDFArray>(qMove(array)));
}
default:
return object;
}
}
QByteArray PDFStringRef::getString() const
{
if (inplaceString)

View File

@ -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

View File

@ -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

View File

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

View File

@ -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)

210
PdfTool/pdftoolunite.cpp Normal file
View File

@ -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 <https://www.gnu.org/licenses/>.
#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<pdf::PDFObjectReference> 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<pdf::PDFObjectReference> currentPages = temporaryBuilder.getPages();
std::vector<pdf::PDFObjectReference> 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<pdf::PDFObjectReference> 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

36
PdfTool/pdftoolunite.h Normal file
View File

@ -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 <https://www.gnu.org/licenses/>.
#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

View File

@ -5763,6 +5763,54 @@ return annotationObject;</property>
<property name="functionDescription">This function is used to create a new trailer dictionary, when blank document is created. Do not call this function manually.</property>
<property name="returnType">_PDFObject</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::GeneratedPDFObject">
<property name="objectName"></property>
<property name="items">
<QObject class="codegen::GeneratedPDFObject">
<property name="objectName"></property>
<property name="items"/>
<property name="dictionaryItemName">OpenAction</property>
<property name="objectType">DictionaryItemSimple</property>
<property name="value">PDFObject()</property>
</QObject>
<QObject class="codegen::GeneratedPDFObject">
<property name="objectName"></property>
<property name="items"/>
<property name="dictionaryItemName">AA</property>
<property name="objectType">DictionaryItemSimple</property>
<property name="value">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">removeDocumentActions</property>
<property name="functionDescription">Removes document actions from document catalog.</property>
<property name="returnType">_void</property>
</QObject>
<QObject class="codegen::GeneratedFunction">
<property name="objectName"></property>
<property name="items">
@ -5804,6 +5852,95 @@ return annotationObject;</property>
<property name="functionDescription">Removes outline tree from document catalog.</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::GeneratedPDFObject">
<property name="objectName"></property>
<property name="items">
<QObject class="codegen::GeneratedPDFObject">
<property name="objectName"></property>
<property name="items"/>
<property name="dictionaryItemName">StructTreeRoot</property>
<property name="objectType">DictionaryItemSimple</property>
<property name="value">PDFObject()</property>
</QObject>
<QObject class="codegen::GeneratedPDFObject">
<property name="objectName"></property>
<property name="items"/>
<property name="dictionaryItemName">MarkInfo</property>
<property name="objectType">DictionaryItemSimple</property>
<property name="value">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">removeStructureTree</property>
<property name="functionDescription">Removes structure tree from document catalog.</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::GeneratedPDFObject">
<property name="objectName"></property>
<property name="items">
<QObject class="codegen::GeneratedPDFObject">
<property name="objectName"></property>
<property name="items"/>
<property name="dictionaryItemName">Threads</property>
<property name="objectType">DictionaryItemSimple</property>
<property name="value">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">removeThreads</property>
<property name="functionDescription">Removes threads from document catalog.</property>
<property name="returnType">_void</property>
</QObject>
<QObject class="codegen::GeneratedFunction">
<property name="objectName"></property>
<property name="items">
@ -7336,44 +7473,19 @@ updateDocumentInfo(qMove(updatedInfoDictionary));</property>
<QObject class="codegen::GeneratedAction">
<property name="objectName"></property>
<property name="items">
<QObject class="codegen::GeneratedPDFObject">
<QObject class="codegen::GeneratedParameter">
<property name="objectName"></property>
<property name="items">
<QObject class="codegen::GeneratedPDFObject">
<property name="objectName"></property>
<property name="items"/>
<property name="dictionaryItemName">Threads</property>
<property name="objectType">DictionaryItemSimple</property>
<property name="value">PDFObject()</property>
</QObject>
</property>
<property name="dictionaryItemName"></property>
<property name="objectType">Dictionary</property>
<property name="value"></property>
<property name="items"/>
<property name="parameterName">ocProperties</property>
<property name="parameterType">_PDFObjectReference</property>
<property name="parameterDescription">Reference to catalog optional content properties.</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="actionType">Parameters</property>
<property name="variableName"></property>
<property name="variableType">_void</property>
<property name="code">mergeTo(getCatalogReference(), updatedCatalog);</property>
<property name="code"></property>
</QObject>
</property>
<property name="functionType">Structure</property>
<property name="functionName">removeThreads</property>
<property name="functionDescription">Removes threads from document catalog.</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">
@ -7383,16 +7495,9 @@ updateDocumentInfo(qMove(updatedInfoDictionary));</property>
<QObject class="codegen::GeneratedPDFObject">
<property name="objectName"></property>
<property name="items"/>
<property name="dictionaryItemName">OpenAction</property>
<property name="dictionaryItemName">OCProperties</property>
<property name="objectType">DictionaryItemSimple</property>
<property name="value">PDFObject()</property>
</QObject>
<QObject class="codegen::GeneratedPDFObject">
<property name="objectName"></property>
<property name="items"/>
<property name="dictionaryItemName">AA</property>
<property name="objectType">DictionaryItemSimple</property>
<property name="value">PDFObject()</property>
<property name="value">ocProperties</property>
</QObject>
</property>
<property name="dictionaryItemName"></property>
@ -7415,56 +7520,8 @@ updateDocumentInfo(qMove(updatedInfoDictionary));</property>
</QObject>
</property>
<property name="functionType">Structure</property>
<property name="functionName">removeDocumentActions</property>
<property name="functionDescription">Removes document actions from document catalog.</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::GeneratedPDFObject">
<property name="objectName"></property>
<property name="items">
<QObject class="codegen::GeneratedPDFObject">
<property name="objectName"></property>
<property name="items"/>
<property name="dictionaryItemName">StructTreeRoot</property>
<property name="objectType">DictionaryItemSimple</property>
<property name="value">PDFObject()</property>
</QObject>
<QObject class="codegen::GeneratedPDFObject">
<property name="objectName"></property>
<property name="items"/>
<property name="dictionaryItemName">MarkInfo</property>
<property name="objectType">DictionaryItemSimple</property>
<property name="value">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">removeStructureTree</property>
<property name="functionDescription">Removes structure tree from document catalog.</property>
<property name="functionName">setCatalogOptionalContentProperties</property>
<property name="functionDescription">Set optional content properties to catalog.</property>
<property name="returnType">_void</property>
</QObject>
</property>