mirror of
https://github.com/JakubMelka/PDF4QT.git
synced 2025-03-30 11:10:06 +02:00
Unite tool (basics)
This commit is contained in:
parent
c58158e3ee
commit
5d5973772a
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
210
PdfTool/pdftoolunite.cpp
Normal 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
36
PdfTool/pdftoolunite.h
Normal 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
|
@ -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>
|
||||
|
Loading…
x
Reference in New Issue
Block a user