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/.. | 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 | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user