mirror of
https://github.com/JakubMelka/PDF4QT.git
synced 2025-03-24 23:30:25 +01:00
Optional content GUI
This commit is contained in:
parent
445da73b1c
commit
d4ef618c5d
@ -36,6 +36,7 @@ DEFINES += QT_DEPRECATED_WARNINGS
|
|||||||
DESTDIR = $$OUT_PWD/..
|
DESTDIR = $$OUT_PWD/..
|
||||||
|
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
|
sources/pdfitemmodels.cpp \
|
||||||
sources/pdfobject.cpp \
|
sources/pdfobject.cpp \
|
||||||
sources/pdfoptionalcontent.cpp \
|
sources/pdfoptionalcontent.cpp \
|
||||||
sources/pdfparser.cpp \
|
sources/pdfparser.cpp \
|
||||||
@ -61,6 +62,7 @@ SOURCES += \
|
|||||||
sources/pdfimage.cpp
|
sources/pdfimage.cpp
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
|
sources/pdfitemmodels.h \
|
||||||
sources/pdfobject.h \
|
sources/pdfobject.h \
|
||||||
sources/pdfoptionalcontent.h \
|
sources/pdfoptionalcontent.h \
|
||||||
sources/pdfparser.h \
|
sources/pdfparser.h \
|
||||||
|
275
PdfForQtLib/sources/pdfitemmodels.cpp
Normal file
275
PdfForQtLib/sources/pdfitemmodels.cpp
Normal file
@ -0,0 +1,275 @@
|
|||||||
|
// Copyright (C) 2019 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 "pdfitemmodels.h"
|
||||||
|
#include "pdfdocument.h"
|
||||||
|
|
||||||
|
namespace pdf
|
||||||
|
{
|
||||||
|
|
||||||
|
PDFTreeItem::~PDFTreeItem()
|
||||||
|
{
|
||||||
|
qDeleteAll(m_children);
|
||||||
|
}
|
||||||
|
|
||||||
|
PDFTreeItemModel::PDFTreeItemModel(QObject* parent) :
|
||||||
|
QAbstractItemModel(parent),
|
||||||
|
m_document(nullptr)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void PDFTreeItemModel::setDocument(const PDFDocument* document)
|
||||||
|
{
|
||||||
|
if (m_document != document)
|
||||||
|
{
|
||||||
|
m_document = document;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PDFTreeItemModel::isEmpty() const
|
||||||
|
{
|
||||||
|
return rowCount(QModelIndex()) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndex PDFTreeItemModel::index(int row, int column, const QModelIndex& parent) const
|
||||||
|
{
|
||||||
|
if (hasIndex(row, column, parent))
|
||||||
|
{
|
||||||
|
const PDFTreeItem* parentItem = nullptr;
|
||||||
|
|
||||||
|
if (!parent.isValid())
|
||||||
|
{
|
||||||
|
parentItem = m_rootItem.get();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
parentItem = static_cast<PDFTreeItem*>(parent.internalPointer());
|
||||||
|
}
|
||||||
|
|
||||||
|
return createIndex(row, column, const_cast<PDFTreeItem*>(parentItem->getChild(row)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return QModelIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndex PDFTreeItemModel::parent(const QModelIndex& child) const
|
||||||
|
{
|
||||||
|
if (child.isValid())
|
||||||
|
{
|
||||||
|
const PDFTreeItem* childItem = static_cast<PDFTreeItem*>(child.internalPointer());
|
||||||
|
const PDFTreeItem* parentItem = childItem->getParent();
|
||||||
|
|
||||||
|
if (parentItem != m_rootItem.get())
|
||||||
|
{
|
||||||
|
return createIndex(parentItem->getRow(), child.column(), const_cast<PDFTreeItem*>(parentItem));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return QModelIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
int PDFTreeItemModel::rowCount(const QModelIndex& parent) const
|
||||||
|
{
|
||||||
|
if (parent.isValid())
|
||||||
|
{
|
||||||
|
const PDFTreeItem* parentItem = static_cast<PDFTreeItem*>(parent.internalPointer());
|
||||||
|
return parentItem->getChildCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_rootItem ? m_rootItem->getChildCount() : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PDFTreeItemModel::hasChildren(const QModelIndex& parent) const
|
||||||
|
{
|
||||||
|
return rowCount(parent) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Qt::ItemFlags PDFTreeItemModel::flags(const QModelIndex& index) const
|
||||||
|
{
|
||||||
|
if (!index.isValid())
|
||||||
|
{
|
||||||
|
return Qt::NoItemFlags;
|
||||||
|
}
|
||||||
|
|
||||||
|
return QAbstractItemModel::flags(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int PDFOptionalContentTreeItemModel::columnCount(const QModelIndex& parent) const
|
||||||
|
{
|
||||||
|
Q_UNUSED(parent);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant PDFOptionalContentTreeItemModel::data(const QModelIndex& index, int role) const
|
||||||
|
{
|
||||||
|
if (!index.isValid())
|
||||||
|
{
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
const PDFOptionalContentTreeItem* item = static_cast<const PDFOptionalContentTreeItem*>(index.internalPointer());
|
||||||
|
switch (role)
|
||||||
|
{
|
||||||
|
case Qt::DisplayRole:
|
||||||
|
return item->getText();
|
||||||
|
|
||||||
|
case Qt::CheckStateRole:
|
||||||
|
return Qt::Checked;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PDFOptionalContentTreeItemModel::update()
|
||||||
|
{
|
||||||
|
beginResetModel();
|
||||||
|
|
||||||
|
PDFOptionalContentTreeItem* root = new PDFOptionalContentTreeItem(nullptr, PDFObjectReference(), QString(), false);
|
||||||
|
m_rootItem.reset(root);
|
||||||
|
|
||||||
|
if (m_document)
|
||||||
|
{
|
||||||
|
const PDFOptionalContentProperties* optionalContentProperties = m_document->getCatalog()->getOptionalContentProperties();
|
||||||
|
if (optionalContentProperties->isValid())
|
||||||
|
{
|
||||||
|
const PDFOptionalContentConfiguration& configuration = optionalContentProperties->getDefaultConfiguration();
|
||||||
|
const PDFObject& orderObject = m_document->getObject(configuration.getOrder());
|
||||||
|
const std::vector<PDFObjectReference>& ocgs = optionalContentProperties->getAllOptionalContentGroups();
|
||||||
|
const std::vector<PDFObjectReference>& locked = configuration.getLocked();
|
||||||
|
|
||||||
|
// We must detect cycles in the reference array
|
||||||
|
std::set<PDFObjectReference> lockedOptionalContentGroups(locked.cbegin(), locked.cend());
|
||||||
|
std::set<PDFObjectReference> optionalContentGroups(ocgs.cbegin(), ocgs.cend());
|
||||||
|
std::set<PDFObjectReference> processedReferences;
|
||||||
|
|
||||||
|
PDFDocumentDataLoaderDecorator loader(m_document);
|
||||||
|
std::function<PDFOptionalContentTreeItem*(const PDFObject&)> processObject = [&, this](const PDFObject& object) -> PDFOptionalContentTreeItem*
|
||||||
|
{
|
||||||
|
PDFObject dereferencedObject = object;
|
||||||
|
if (object.isReference())
|
||||||
|
{
|
||||||
|
PDFObjectReference reference = object.getReference();
|
||||||
|
if (optionalContentGroups.count(reference))
|
||||||
|
{
|
||||||
|
const PDFOptionalContentGroup& ocg = optionalContentProperties->getOptionalContentGroup(reference);
|
||||||
|
return new PDFOptionalContentTreeItem(nullptr, reference, ocg.getName(), lockedOptionalContentGroups.count(reference));
|
||||||
|
}
|
||||||
|
else if (!processedReferences.count(reference))
|
||||||
|
{
|
||||||
|
processedReferences.insert(reference);
|
||||||
|
dereferencedObject = m_document->getStorage().getObject(reference);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Error - we have cyclic references
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dereferencedObject.isArray())
|
||||||
|
{
|
||||||
|
const PDFArray* array = dereferencedObject.getArray();
|
||||||
|
const size_t arraySize = array->getCount();
|
||||||
|
|
||||||
|
// We must have at least one item!
|
||||||
|
if (arraySize == 0)
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString text;
|
||||||
|
size_t i = 0;
|
||||||
|
|
||||||
|
// Try to retrieve group name
|
||||||
|
const PDFObject& firstItem = m_document->getObject(array->getItem(0));
|
||||||
|
if (firstItem.isString())
|
||||||
|
{
|
||||||
|
text = loader.readTextString(firstItem, QString());
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<PDFOptionalContentTreeItem> parentItem(new PDFOptionalContentTreeItem(nullptr, PDFObjectReference(), text, false));
|
||||||
|
for (; i < arraySize; ++i)
|
||||||
|
{
|
||||||
|
if (PDFOptionalContentTreeItem* item = processObject(array->getItem(i)))
|
||||||
|
{
|
||||||
|
parentItem->addCreatedChild(item);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Item cannot be parsed properly
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return parentItem.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
m_rootItem.reset(processObject(orderObject));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
endResetModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
Qt::ItemFlags PDFOptionalContentTreeItemModel::flags(const QModelIndex& index) const
|
||||||
|
{
|
||||||
|
Qt::ItemFlags flags = PDFTreeItemModel::flags(index);
|
||||||
|
|
||||||
|
if (!index.isValid())
|
||||||
|
{
|
||||||
|
return flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
const PDFOptionalContentTreeItem* item = static_cast<const PDFOptionalContentTreeItem*>(index.internalPointer());
|
||||||
|
if (item->getReference() != PDFObjectReference())
|
||||||
|
{
|
||||||
|
flags = flags | Qt::ItemIsUserCheckable | Qt::ItemNeverHasChildren;
|
||||||
|
|
||||||
|
if (item->isLocked())
|
||||||
|
{
|
||||||
|
flags &= ~Qt::ItemIsEnabled;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString PDFOptionalContentTreeItem::getText() const
|
||||||
|
{
|
||||||
|
if (!m_text.isEmpty())
|
||||||
|
{
|
||||||
|
return m_text;
|
||||||
|
}
|
||||||
|
else if (getParent())
|
||||||
|
{
|
||||||
|
return static_cast<const PDFOptionalContentTreeItem*>(getParent())->getText();
|
||||||
|
}
|
||||||
|
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace pdf
|
123
PdfForQtLib/sources/pdfitemmodels.h
Normal file
123
PdfForQtLib/sources/pdfitemmodels.h
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
// Copyright (C) 2019 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 PDFITEMMODELS_H
|
||||||
|
#define PDFITEMMODELS_H
|
||||||
|
|
||||||
|
#include "pdfglobal.h"
|
||||||
|
#include "pdfobject.h"
|
||||||
|
|
||||||
|
#include <QAbstractItemModel>
|
||||||
|
|
||||||
|
namespace pdf
|
||||||
|
{
|
||||||
|
class PDFDocument;
|
||||||
|
|
||||||
|
/// Represents tree item in the GUI tree
|
||||||
|
class PDFTreeItem
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
inline explicit PDFTreeItem() = default;
|
||||||
|
inline explicit PDFTreeItem(PDFTreeItem* parent) : m_parent(parent) { }
|
||||||
|
virtual ~PDFTreeItem();
|
||||||
|
|
||||||
|
template<typename T, typename... Arguments>
|
||||||
|
inline T* addChild(Arguments&&... arguments)
|
||||||
|
{
|
||||||
|
T* item = new T(this, std::forward(arguments)...);
|
||||||
|
m_children.push_back(item);
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
void addCreatedChild(PDFTreeItem* item)
|
||||||
|
{
|
||||||
|
item->m_parent = this;
|
||||||
|
m_children.push_back(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
int getRow() const { return m_parent->m_children.indexOf(const_cast<PDFTreeItem*>(this)); }
|
||||||
|
int getChildCount() const { return m_children.size(); }
|
||||||
|
const PDFTreeItem* getChild(int index) const { return m_children.at(index); }
|
||||||
|
const PDFTreeItem* getParent() const { return m_parent; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
PDFTreeItem* m_parent = nullptr;
|
||||||
|
QList<PDFTreeItem*> m_children;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Root of all tree item models
|
||||||
|
class PDFFORQTLIBSHARED_EXPORT PDFTreeItemModel : public QAbstractItemModel
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit PDFTreeItemModel(QObject* parent);
|
||||||
|
|
||||||
|
void setDocument(const pdf::PDFDocument* document);
|
||||||
|
|
||||||
|
bool isEmpty() const;
|
||||||
|
|
||||||
|
virtual QModelIndex index(int row, int column, const QModelIndex& parent) const override;
|
||||||
|
virtual QModelIndex parent(const QModelIndex& child) const override;
|
||||||
|
virtual int rowCount(const QModelIndex& parent) const override;
|
||||||
|
virtual bool hasChildren(const QModelIndex& parent) const override;
|
||||||
|
virtual Qt::ItemFlags flags(const QModelIndex& index) const override;
|
||||||
|
virtual void update() = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
const PDFDocument* m_document;
|
||||||
|
std::unique_ptr<PDFTreeItem> m_rootItem;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PDFOptionalContentTreeItem : public PDFTreeItem
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
inline explicit PDFOptionalContentTreeItem(PDFOptionalContentTreeItem* parent, PDFObjectReference reference, QString text, bool locked) :
|
||||||
|
PDFTreeItem(parent),
|
||||||
|
m_reference(reference),
|
||||||
|
m_text(qMove(text)),
|
||||||
|
m_locked(locked)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
PDFObjectReference getReference() const { return m_reference; }
|
||||||
|
QString getText() const;
|
||||||
|
bool isLocked() const { return m_locked; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
PDFObjectReference m_reference; ///< Reference to optional content group
|
||||||
|
QString m_text; ///< Node display name
|
||||||
|
bool m_locked; ///< Node is locked (user can't change it)
|
||||||
|
};
|
||||||
|
|
||||||
|
class PDFFORQTLIBSHARED_EXPORT PDFOptionalContentTreeItemModel : public PDFTreeItemModel
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
inline explicit PDFOptionalContentTreeItemModel(QObject* parent) :
|
||||||
|
PDFTreeItemModel(parent)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual int columnCount(const QModelIndex& parent) const override;
|
||||||
|
virtual QVariant data(const QModelIndex& index, int role) const override;
|
||||||
|
virtual void update() override;
|
||||||
|
virtual Qt::ItemFlags flags(const QModelIndex& index) const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace pdf
|
||||||
|
|
||||||
|
#endif // PDFITEMMODELS_H
|
@ -34,6 +34,15 @@ PDFOptionalContentProperties PDFOptionalContentProperties::create(const PDFDocum
|
|||||||
PDFDocumentDataLoaderDecorator loader(document);
|
PDFDocumentDataLoaderDecorator loader(document);
|
||||||
properties.m_allOptionalContentGroups = loader.readReferenceArrayFromDictionary(dictionary, "OCGs");
|
properties.m_allOptionalContentGroups = loader.readReferenceArrayFromDictionary(dictionary, "OCGs");
|
||||||
|
|
||||||
|
for (const PDFObjectReference& reference : properties.m_allOptionalContentGroups)
|
||||||
|
{
|
||||||
|
const PDFObject& object = document->getStorage().getObject(reference);
|
||||||
|
if (!object.isNull())
|
||||||
|
{
|
||||||
|
properties.m_optionalContentGroups[reference] = PDFOptionalContentGroup::create(document, object);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (dictionary->hasKey("D"))
|
if (dictionary->hasKey("D"))
|
||||||
{
|
{
|
||||||
properties.m_defaultConfiguration = PDFOptionalContentConfiguration::create(document, dictionary->get("D"));
|
properties.m_defaultConfiguration = PDFOptionalContentConfiguration::create(document, dictionary->get("D"));
|
||||||
@ -178,4 +187,91 @@ PDFOptionalContentConfiguration::UsageApplication PDFOptionalContentConfiguratio
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PDFOptionalContentGroup::PDFOptionalContentGroup() :
|
||||||
|
m_usageZoomMin(0),
|
||||||
|
m_usageZoomMax(std::numeric_limits<PDFReal>::infinity()),
|
||||||
|
m_usagePrintState(OCState::Unknown),
|
||||||
|
m_usageViewState(OCState::Unknown),
|
||||||
|
m_usageExportState(OCState::Unknown)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
PDFOptionalContentGroup PDFOptionalContentGroup::create(const PDFDocument* document, const PDFObject& object)
|
||||||
|
{
|
||||||
|
PDFOptionalContentGroup result;
|
||||||
|
|
||||||
|
const PDFObject& dereferencedObject = document->getObject(object);
|
||||||
|
if (!dereferencedObject.isDictionary())
|
||||||
|
{
|
||||||
|
throw PDFParserException(PDFTranslationContext::tr("Invalid optional content group."));
|
||||||
|
}
|
||||||
|
|
||||||
|
PDFDocumentDataLoaderDecorator loader(document);
|
||||||
|
|
||||||
|
const PDFDictionary* dictionary = dereferencedObject.getDictionary();
|
||||||
|
result.m_name = loader.readTextStringFromDictionary(dictionary, "Name", QString());
|
||||||
|
|
||||||
|
if (dictionary->hasKey("Intent"))
|
||||||
|
{
|
||||||
|
const PDFObject& nameOrNames = document->getObject(dictionary->get("Intent"));
|
||||||
|
|
||||||
|
if (nameOrNames.isName())
|
||||||
|
{
|
||||||
|
result.m_intents = { loader.readName(nameOrNames) };
|
||||||
|
}
|
||||||
|
else if (nameOrNames.isArray())
|
||||||
|
{
|
||||||
|
result.m_intents = loader.readNameArray(nameOrNames);
|
||||||
|
}
|
||||||
|
else if (!nameOrNames.isNull())
|
||||||
|
{
|
||||||
|
throw PDFParserException(PDFTranslationContext::tr("Invalid optional content group."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const PDFObject& usageDictionaryObject = dictionary->get("Usage");
|
||||||
|
if (usageDictionaryObject.isDictionary())
|
||||||
|
{
|
||||||
|
const PDFDictionary* usageDictionary = usageDictionaryObject.getDictionary();
|
||||||
|
|
||||||
|
result.m_creatorInfo = document->getObject(usageDictionary->get("CreatorInfo"));
|
||||||
|
result.m_language = document->getObject(usageDictionary->get("Language"));
|
||||||
|
|
||||||
|
const PDFObject& zoomDictionary = document->getObject(usageDictionary->get("Zoom"));
|
||||||
|
if (zoomDictionary.isDictionary())
|
||||||
|
{
|
||||||
|
result.m_usageZoomMin = loader.readNumberFromDictionary(usageDictionary, "min", result.m_usageZoomMin);
|
||||||
|
result.m_usageZoomMax = loader.readNumberFromDictionary(usageDictionary, "max", result.m_usageZoomMax);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto readState = [document, usageDictionary, &loader](const char* dictionaryKey, const char* key) -> OCState
|
||||||
|
{
|
||||||
|
const PDFObject& stateDictionaryObject = document->getObject(usageDictionary->get(dictionaryKey));
|
||||||
|
if (stateDictionaryObject.isDictionary())
|
||||||
|
{
|
||||||
|
const PDFDictionary* stateDictionary = stateDictionaryObject.getDictionary();
|
||||||
|
QByteArray stateName = loader.readNameFromDictionary(stateDictionary, key);
|
||||||
|
|
||||||
|
if (stateName == "ON")
|
||||||
|
{
|
||||||
|
return OCState::ON;
|
||||||
|
}
|
||||||
|
if (stateName == "OFF")
|
||||||
|
{
|
||||||
|
return OCState::OFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return OCState::Unknown;
|
||||||
|
};
|
||||||
|
|
||||||
|
result.m_usageViewState = readState("View", "ViewState");
|
||||||
|
result.m_usagePrintState = readState("Print", "PrintState");
|
||||||
|
result.m_usageExportState = readState("Export", "ExportState");
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace pdf
|
} // namespace pdf
|
||||||
|
@ -26,6 +26,42 @@ namespace pdf
|
|||||||
|
|
||||||
class PDFDocument;
|
class PDFDocument;
|
||||||
|
|
||||||
|
/// State of the optional content group, or result of expression
|
||||||
|
enum class OCState
|
||||||
|
{
|
||||||
|
ON,
|
||||||
|
OFF,
|
||||||
|
Unknown
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr OCState operator &(OCState left, OCState right)
|
||||||
|
{
|
||||||
|
if (left == OCState::Unknown)
|
||||||
|
{
|
||||||
|
return right;
|
||||||
|
}
|
||||||
|
if (right == OCState::Unknown)
|
||||||
|
{
|
||||||
|
return left;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (left == OCState::ON && right == OCState::ON) ? OCState::ON : OCState::OFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr OCState operator |(OCState left, OCState right)
|
||||||
|
{
|
||||||
|
if (left == OCState::Unknown)
|
||||||
|
{
|
||||||
|
return right;
|
||||||
|
}
|
||||||
|
if (right == OCState::Unknown)
|
||||||
|
{
|
||||||
|
return left;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (left == OCState::ON || right == OCState::ON) ? OCState::ON : OCState::OFF;
|
||||||
|
}
|
||||||
|
|
||||||
/// Configuration of optional content configuration.
|
/// Configuration of optional content configuration.
|
||||||
class PDFOptionalContentConfiguration
|
class PDFOptionalContentConfiguration
|
||||||
{
|
{
|
||||||
@ -57,6 +93,18 @@ public:
|
|||||||
/// \param object Object containing documents optional content configuration
|
/// \param object Object containing documents optional content configuration
|
||||||
static PDFOptionalContentConfiguration create(const PDFDocument* document, const PDFObject& object);
|
static PDFOptionalContentConfiguration create(const PDFDocument* document, const PDFObject& object);
|
||||||
|
|
||||||
|
const QString& getName() const { return m_name; }
|
||||||
|
const QString& getCreator() const { return m_creator; }
|
||||||
|
BaseState getBaseState() const { return m_baseState; }
|
||||||
|
const std::vector<PDFObjectReference>& getOnArray() const { return m_OnArray; }
|
||||||
|
const std::vector<PDFObjectReference>& getOffArray() const { return m_OffArray; }
|
||||||
|
const std::vector<QByteArray>& getIntents() const { return m_intents; }
|
||||||
|
const std::vector<UsageApplication>& getUsageApplications() const { return m_usageApplications; }
|
||||||
|
const PDFObject& getOrder() const { return m_order; }
|
||||||
|
ListMode getListMode() const { return m_listMode; }
|
||||||
|
const std::vector<std::vector<PDFObjectReference>>& getRadioButtonGroups() const { return m_radioButtonGroups; }
|
||||||
|
const std::vector<PDFObjectReference>& getLocked() const { return m_locked; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Creates usage application
|
/// Creates usage application
|
||||||
/// \param document Document
|
/// \param document Document
|
||||||
@ -76,6 +124,43 @@ private:
|
|||||||
std::vector<PDFObjectReference> m_locked;
|
std::vector<PDFObjectReference> m_locked;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Class reprezenting optional content group - it contains properties of the group,
|
||||||
|
/// such as name, usage etc.
|
||||||
|
class PDFOptionalContentGroup
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit PDFOptionalContentGroup();
|
||||||
|
|
||||||
|
/// Creates optional content group from the object. Object must be valid optional
|
||||||
|
/// content group, if it is invalid, then exception is thrown.
|
||||||
|
/// \param document Document
|
||||||
|
/// \param object Object containing optional content group
|
||||||
|
static PDFOptionalContentGroup create(const PDFDocument* document, const PDFObject& object);
|
||||||
|
|
||||||
|
PDFObjectReference getReference() const { return m_reference; }
|
||||||
|
const QString& getName() const { return m_name; }
|
||||||
|
const std::vector<QByteArray>& getIntents() const { return m_intents; }
|
||||||
|
PDFObject getCreatorInfo() const { return m_creatorInfo; }
|
||||||
|
PDFObject getLanguage() const { return m_language; }
|
||||||
|
PDFReal getUsageZoomMin() const { return m_usageZoomMin; }
|
||||||
|
PDFReal getUsageZoomMax() const { return m_usageZoomMax; }
|
||||||
|
OCState getUsagePrintState() const { return m_usagePrintState; }
|
||||||
|
OCState getUsageViewState() const { return m_usageViewState; }
|
||||||
|
OCState getUsageExportState() const { return m_usageExportState; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
PDFObjectReference m_reference;
|
||||||
|
QString m_name;
|
||||||
|
std::vector<QByteArray> m_intents;
|
||||||
|
PDFObject m_creatorInfo;
|
||||||
|
PDFObject m_language;
|
||||||
|
PDFReal m_usageZoomMin;
|
||||||
|
PDFReal m_usageZoomMax;
|
||||||
|
OCState m_usagePrintState;
|
||||||
|
OCState m_usageViewState;
|
||||||
|
OCState m_usageExportState;
|
||||||
|
};
|
||||||
|
|
||||||
/// Object containing properties of the optional content of the PDF document. It contains
|
/// Object containing properties of the optional content of the PDF document. It contains
|
||||||
/// for example all documents optional content groups.
|
/// for example all documents optional content groups.
|
||||||
class PDFOptionalContentProperties
|
class PDFOptionalContentProperties
|
||||||
@ -84,7 +169,7 @@ public:
|
|||||||
explicit PDFOptionalContentProperties() = default;
|
explicit PDFOptionalContentProperties() = default;
|
||||||
|
|
||||||
/// Returns, if object is valid - at least one optional content group exists
|
/// Returns, if object is valid - at least one optional content group exists
|
||||||
bool isValid() const { return !m_allOptionalContentGroups.empty(); }
|
bool isValid() const { return !m_allOptionalContentGroups.empty() && m_allOptionalContentGroups.size() == m_optionalContentGroups.size(); }
|
||||||
|
|
||||||
/// Creates new optional content properties from the object. If object is not valid,
|
/// Creates new optional content properties from the object. If object is not valid,
|
||||||
/// then exception is thrown.
|
/// then exception is thrown.
|
||||||
@ -92,10 +177,15 @@ public:
|
|||||||
/// \param object Object containing documents optional content properties
|
/// \param object Object containing documents optional content properties
|
||||||
static PDFOptionalContentProperties create(const PDFDocument* document, const PDFObject& object);
|
static PDFOptionalContentProperties create(const PDFDocument* document, const PDFObject& object);
|
||||||
|
|
||||||
|
const std::vector<PDFObjectReference>& getAllOptionalContentGroups() const { return m_allOptionalContentGroups; }
|
||||||
|
const PDFOptionalContentConfiguration& getDefaultConfiguration() const { return m_defaultConfiguration; }
|
||||||
|
const PDFOptionalContentGroup& getOptionalContentGroup(PDFObjectReference reference) const { return m_optionalContentGroups.at(reference); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<PDFObjectReference> m_allOptionalContentGroups;
|
std::vector<PDFObjectReference> m_allOptionalContentGroups;
|
||||||
PDFOptionalContentConfiguration m_defaultConfiguration;
|
PDFOptionalContentConfiguration m_defaultConfiguration;
|
||||||
std::vector<PDFOptionalContentConfiguration> m_configurations;
|
std::vector<PDFOptionalContentConfiguration> m_configurations;
|
||||||
|
std::map<PDFObjectReference, PDFOptionalContentGroup> m_optionalContentGroups;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace pdf
|
} // namespace pdf
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
#include "pdfdrawspacecontroller.h"
|
#include "pdfdrawspacecontroller.h"
|
||||||
#include "pdfrenderingerrorswidget.h"
|
#include "pdfrenderingerrorswidget.h"
|
||||||
#include "pdffont.h"
|
#include "pdffont.h"
|
||||||
|
#include "pdfitemmodels.h"
|
||||||
|
|
||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
@ -33,6 +34,10 @@
|
|||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QDesktopWidget>
|
#include <QDesktopWidget>
|
||||||
#include <QStandardPaths>
|
#include <QStandardPaths>
|
||||||
|
#include <QDockWidget>
|
||||||
|
#include <QTreeView>
|
||||||
|
#include <QLayout>
|
||||||
|
#include <QHeaderView>
|
||||||
|
|
||||||
namespace pdfviewer
|
namespace pdfviewer
|
||||||
{
|
{
|
||||||
@ -40,7 +45,10 @@ namespace pdfviewer
|
|||||||
PDFViewerMainWindow::PDFViewerMainWindow(QWidget *parent) :
|
PDFViewerMainWindow::PDFViewerMainWindow(QWidget *parent) :
|
||||||
QMainWindow(parent),
|
QMainWindow(parent),
|
||||||
ui(new Ui::PDFViewerMainWindow),
|
ui(new Ui::PDFViewerMainWindow),
|
||||||
m_pdfWidget(nullptr)
|
m_pdfWidget(nullptr),
|
||||||
|
m_optionalContentDockWidget(nullptr),
|
||||||
|
m_optionalContentTreeView(nullptr),
|
||||||
|
m_optionalContentTreeModel(nullptr)
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
|
|
||||||
@ -77,6 +85,19 @@ PDFViewerMainWindow::PDFViewerMainWindow(QWidget *parent) :
|
|||||||
setCentralWidget(m_pdfWidget);
|
setCentralWidget(m_pdfWidget);
|
||||||
setFocusProxy(m_pdfWidget);
|
setFocusProxy(m_pdfWidget);
|
||||||
|
|
||||||
|
m_optionalContentDockWidget = new QDockWidget(tr("Optional Content"), this);
|
||||||
|
m_optionalContentDockWidget->setObjectName("OptionalContentDockWidget");
|
||||||
|
m_optionalContentDockWidget->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
|
||||||
|
m_optionalContentTreeView = new QTreeView(m_optionalContentDockWidget);
|
||||||
|
m_optionalContentTreeView->header()->hide();
|
||||||
|
m_optionalContentTreeModel = new pdf::PDFOptionalContentTreeItemModel(m_optionalContentTreeView);
|
||||||
|
m_optionalContentTreeView->setModel(m_optionalContentTreeModel);
|
||||||
|
m_optionalContentDockWidget->setWidget(m_optionalContentTreeView);
|
||||||
|
addDockWidget(Qt::LeftDockWidgetArea, m_optionalContentDockWidget);
|
||||||
|
|
||||||
|
ui->menuView->addSeparator();
|
||||||
|
ui->menuView->addAction(m_optionalContentDockWidget->toggleViewAction());
|
||||||
|
|
||||||
connect(m_pdfWidget->getDrawWidgetProxy(), &pdf::PDFDrawWidgetProxy::pageLayoutChanged, this, &PDFViewerMainWindow::updatePageLayoutActions);
|
connect(m_pdfWidget->getDrawWidgetProxy(), &pdf::PDFDrawWidgetProxy::pageLayoutChanged, this, &PDFViewerMainWindow::updatePageLayoutActions);
|
||||||
connect(m_pdfWidget, &pdf::PDFWidget::pageRenderingErrorsChanged, this, &PDFViewerMainWindow::onPageRenderingErrorsChanged, Qt::QueuedConnection);
|
connect(m_pdfWidget, &pdf::PDFWidget::pageRenderingErrorsChanged, this, &PDFViewerMainWindow::onPageRenderingErrorsChanged, Qt::QueuedConnection);
|
||||||
|
|
||||||
@ -234,6 +255,18 @@ void PDFViewerMainWindow::openDocument(const QString& fileName)
|
|||||||
void PDFViewerMainWindow::setDocument(const pdf::PDFDocument* document)
|
void PDFViewerMainWindow::setDocument(const pdf::PDFDocument* document)
|
||||||
{
|
{
|
||||||
m_pdfWidget->setDocument(document);
|
m_pdfWidget->setDocument(document);
|
||||||
|
m_optionalContentTreeModel->setDocument(document);
|
||||||
|
m_optionalContentTreeView->expandAll();
|
||||||
|
|
||||||
|
if (m_optionalContentTreeModel->isEmpty())
|
||||||
|
{
|
||||||
|
m_optionalContentDockWidget->hide();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_optionalContentDockWidget->show();
|
||||||
|
}
|
||||||
|
|
||||||
updateTitle();
|
updateTitle();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
#include "pdfcatalog.h"
|
#include "pdfcatalog.h"
|
||||||
#include "pdfrenderer.h"
|
#include "pdfrenderer.h"
|
||||||
|
|
||||||
|
#include <QTreeView>
|
||||||
#include <QMainWindow>
|
#include <QMainWindow>
|
||||||
#include <QSharedPointer>
|
#include <QSharedPointer>
|
||||||
|
|
||||||
@ -33,6 +34,7 @@ namespace pdf
|
|||||||
{
|
{
|
||||||
class PDFWidget;
|
class PDFWidget;
|
||||||
class PDFDocument;
|
class PDFDocument;
|
||||||
|
class PDFOptionalContentTreeItemModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace pdfviewer
|
namespace pdfviewer
|
||||||
@ -82,6 +84,9 @@ private:
|
|||||||
QSharedPointer<pdf::PDFDocument> m_pdfDocument;
|
QSharedPointer<pdf::PDFDocument> m_pdfDocument;
|
||||||
QString m_directory;
|
QString m_directory;
|
||||||
QString m_currentFile;
|
QString m_currentFile;
|
||||||
|
QDockWidget* m_optionalContentDockWidget;
|
||||||
|
QTreeView* m_optionalContentTreeView;
|
||||||
|
pdf::PDFOptionalContentTreeItemModel* m_optionalContentTreeModel;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace pdfviewer
|
} // namespace pdfviewer
|
||||||
|
Loading…
x
Reference in New Issue
Block a user