mirror of
				https://github.com/JakubMelka/PDF4QT.git
				synced 2025-06-05 21:59:17 +02:00 
			
		
		
		
	Optional content GUI
This commit is contained in:
		| @@ -36,6 +36,7 @@ DEFINES += QT_DEPRECATED_WARNINGS | ||||
| DESTDIR = $$OUT_PWD/.. | ||||
|  | ||||
| SOURCES += \ | ||||
|     sources/pdfitemmodels.cpp \ | ||||
|     sources/pdfobject.cpp \ | ||||
|     sources/pdfoptionalcontent.cpp \ | ||||
|     sources/pdfparser.cpp \ | ||||
| @@ -61,6 +62,7 @@ SOURCES += \ | ||||
|     sources/pdfimage.cpp | ||||
|  | ||||
| HEADERS += \ | ||||
|     sources/pdfitemmodels.h \ | ||||
|     sources/pdfobject.h \ | ||||
|     sources/pdfoptionalcontent.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); | ||||
|         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")) | ||||
|         { | ||||
|             properties.m_defaultConfiguration = PDFOptionalContentConfiguration::create(document, dictionary->get("D")); | ||||
| @@ -178,4 +187,91 @@ PDFOptionalContentConfiguration::UsageApplication PDFOptionalContentConfiguratio | ||||
|     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 | ||||
|   | ||||
| @@ -26,6 +26,42 @@ namespace pdf | ||||
|  | ||||
| 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. | ||||
| class PDFOptionalContentConfiguration | ||||
| { | ||||
| @@ -57,6 +93,18 @@ public: | ||||
|     /// \param object Object containing documents optional content configuration | ||||
|     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: | ||||
|     /// Creates usage application | ||||
|     /// \param document Document | ||||
| @@ -76,6 +124,43 @@ private: | ||||
|     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 | ||||
| /// for example all documents optional content groups. | ||||
| class PDFOptionalContentProperties | ||||
| @@ -84,7 +169,7 @@ public: | ||||
|     explicit PDFOptionalContentProperties() = default; | ||||
|  | ||||
|     /// 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, | ||||
|     /// then exception is thrown. | ||||
| @@ -92,10 +177,15 @@ public: | ||||
|     /// \param object Object containing documents optional content properties | ||||
|     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: | ||||
|     std::vector<PDFObjectReference> m_allOptionalContentGroups; | ||||
|     PDFOptionalContentConfiguration m_defaultConfiguration; | ||||
|     std::vector<PDFOptionalContentConfiguration> m_configurations; | ||||
|     std::map<PDFObjectReference, PDFOptionalContentGroup> m_optionalContentGroups; | ||||
| }; | ||||
|  | ||||
| }   // namespace pdf | ||||
|   | ||||
| @@ -25,6 +25,7 @@ | ||||
| #include "pdfdrawspacecontroller.h" | ||||
| #include "pdfrenderingerrorswidget.h" | ||||
| #include "pdffont.h" | ||||
| #include "pdfitemmodels.h" | ||||
|  | ||||
| #include <QSettings> | ||||
| #include <QFileDialog> | ||||
| @@ -33,6 +34,10 @@ | ||||
| #include <QApplication> | ||||
| #include <QDesktopWidget> | ||||
| #include <QStandardPaths> | ||||
| #include <QDockWidget> | ||||
| #include <QTreeView> | ||||
| #include <QLayout> | ||||
| #include <QHeaderView> | ||||
|  | ||||
| namespace pdfviewer | ||||
| { | ||||
| @@ -40,7 +45,10 @@ namespace pdfviewer | ||||
| PDFViewerMainWindow::PDFViewerMainWindow(QWidget *parent) : | ||||
|     QMainWindow(parent), | ||||
|     ui(new Ui::PDFViewerMainWindow), | ||||
|     m_pdfWidget(nullptr) | ||||
|     m_pdfWidget(nullptr), | ||||
|     m_optionalContentDockWidget(nullptr), | ||||
|     m_optionalContentTreeView(nullptr), | ||||
|     m_optionalContentTreeModel(nullptr) | ||||
| { | ||||
|     ui->setupUi(this); | ||||
|  | ||||
| @@ -77,6 +85,19 @@ PDFViewerMainWindow::PDFViewerMainWindow(QWidget *parent) : | ||||
|     setCentralWidget(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, &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) | ||||
| { | ||||
|     m_pdfWidget->setDocument(document); | ||||
|     m_optionalContentTreeModel->setDocument(document); | ||||
|     m_optionalContentTreeView->expandAll(); | ||||
|  | ||||
|     if (m_optionalContentTreeModel->isEmpty()) | ||||
|     { | ||||
|         m_optionalContentDockWidget->hide(); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         m_optionalContentDockWidget->show(); | ||||
|     } | ||||
|  | ||||
|     updateTitle(); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -21,6 +21,7 @@ | ||||
| #include "pdfcatalog.h" | ||||
| #include "pdfrenderer.h" | ||||
|  | ||||
| #include <QTreeView> | ||||
| #include <QMainWindow> | ||||
| #include <QSharedPointer> | ||||
|  | ||||
| @@ -33,6 +34,7 @@ namespace pdf | ||||
| { | ||||
| class PDFWidget; | ||||
| class PDFDocument; | ||||
| class PDFOptionalContentTreeItemModel; | ||||
| } | ||||
|  | ||||
| namespace pdfviewer | ||||
| @@ -82,6 +84,9 @@ private: | ||||
|     QSharedPointer<pdf::PDFDocument> m_pdfDocument; | ||||
|     QString m_directory; | ||||
|     QString m_currentFile; | ||||
|     QDockWidget* m_optionalContentDockWidget; | ||||
|     QTreeView* m_optionalContentTreeView; | ||||
|     pdf::PDFOptionalContentTreeItemModel* m_optionalContentTreeModel; | ||||
| }; | ||||
|  | ||||
| }   // namespace pdfviewer | ||||
|   | ||||
		Reference in New Issue
	
	Block a user