From d4ef618c5d1f60f16deae68a8427a5dc074441d5 Mon Sep 17 00:00:00 2001 From: Jakub Melka Date: Mon, 1 Jul 2019 19:53:38 +0200 Subject: [PATCH] Optional content GUI --- PdfForQtLib/PdfForQtLib.pro | 2 + PdfForQtLib/sources/pdfitemmodels.cpp | 275 +++++++++++++++++++++ PdfForQtLib/sources/pdfitemmodels.h | 123 +++++++++ PdfForQtLib/sources/pdfoptionalcontent.cpp | 96 +++++++ PdfForQtLib/sources/pdfoptionalcontent.h | 92 ++++++- PdfForQtViewer/pdfviewermainwindow.cpp | 35 ++- PdfForQtViewer/pdfviewermainwindow.h | 5 + 7 files changed, 626 insertions(+), 2 deletions(-) create mode 100644 PdfForQtLib/sources/pdfitemmodels.cpp create mode 100644 PdfForQtLib/sources/pdfitemmodels.h diff --git a/PdfForQtLib/PdfForQtLib.pro b/PdfForQtLib/PdfForQtLib.pro index 5beb429..e22c314 100644 --- a/PdfForQtLib/PdfForQtLib.pro +++ b/PdfForQtLib/PdfForQtLib.pro @@ -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 \ diff --git a/PdfForQtLib/sources/pdfitemmodels.cpp b/PdfForQtLib/sources/pdfitemmodels.cpp new file mode 100644 index 0000000..513ec94 --- /dev/null +++ b/PdfForQtLib/sources/pdfitemmodels.cpp @@ -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 . + +#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(parent.internalPointer()); + } + + return createIndex(row, column, const_cast(parentItem->getChild(row))); + } + + return QModelIndex(); +} + +QModelIndex PDFTreeItemModel::parent(const QModelIndex& child) const +{ + if (child.isValid()) + { + const PDFTreeItem* childItem = static_cast(child.internalPointer()); + const PDFTreeItem* parentItem = childItem->getParent(); + + if (parentItem != m_rootItem.get()) + { + return createIndex(parentItem->getRow(), child.column(), const_cast(parentItem)); + } + } + + return QModelIndex(); +} + +int PDFTreeItemModel::rowCount(const QModelIndex& parent) const +{ + if (parent.isValid()) + { + const PDFTreeItem* parentItem = static_cast(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(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& ocgs = optionalContentProperties->getAllOptionalContentGroups(); + const std::vector& locked = configuration.getLocked(); + + // We must detect cycles in the reference array + std::set lockedOptionalContentGroups(locked.cbegin(), locked.cend()); + std::set optionalContentGroups(ocgs.cbegin(), ocgs.cend()); + std::set processedReferences; + + PDFDocumentDataLoaderDecorator loader(m_document); + std::function 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 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(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(getParent())->getText(); + } + + return QString(); +} + +} // namespace pdf diff --git a/PdfForQtLib/sources/pdfitemmodels.h b/PdfForQtLib/sources/pdfitemmodels.h new file mode 100644 index 0000000..f4bc239 --- /dev/null +++ b/PdfForQtLib/sources/pdfitemmodels.h @@ -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 . + +#ifndef PDFITEMMODELS_H +#define PDFITEMMODELS_H + +#include "pdfglobal.h" +#include "pdfobject.h" + +#include + +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 + 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(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 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 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 diff --git a/PdfForQtLib/sources/pdfoptionalcontent.cpp b/PdfForQtLib/sources/pdfoptionalcontent.cpp index fa308c0..25d1715 100644 --- a/PdfForQtLib/sources/pdfoptionalcontent.cpp +++ b/PdfForQtLib/sources/pdfoptionalcontent.cpp @@ -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::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 diff --git a/PdfForQtLib/sources/pdfoptionalcontent.h b/PdfForQtLib/sources/pdfoptionalcontent.h index 8b762e9..dd63598 100644 --- a/PdfForQtLib/sources/pdfoptionalcontent.h +++ b/PdfForQtLib/sources/pdfoptionalcontent.h @@ -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& getOnArray() const { return m_OnArray; } + const std::vector& getOffArray() const { return m_OffArray; } + const std::vector& getIntents() const { return m_intents; } + const std::vector& getUsageApplications() const { return m_usageApplications; } + const PDFObject& getOrder() const { return m_order; } + ListMode getListMode() const { return m_listMode; } + const std::vector>& getRadioButtonGroups() const { return m_radioButtonGroups; } + const std::vector& getLocked() const { return m_locked; } + private: /// Creates usage application /// \param document Document @@ -76,6 +124,43 @@ private: std::vector 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& 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 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& 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 m_allOptionalContentGroups; PDFOptionalContentConfiguration m_defaultConfiguration; std::vector m_configurations; + std::map m_optionalContentGroups; }; } // namespace pdf diff --git a/PdfForQtViewer/pdfviewermainwindow.cpp b/PdfForQtViewer/pdfviewermainwindow.cpp index c8b1c46..8cd6a55 100644 --- a/PdfForQtViewer/pdfviewermainwindow.cpp +++ b/PdfForQtViewer/pdfviewermainwindow.cpp @@ -25,6 +25,7 @@ #include "pdfdrawspacecontroller.h" #include "pdfrenderingerrorswidget.h" #include "pdffont.h" +#include "pdfitemmodels.h" #include #include @@ -33,6 +34,10 @@ #include #include #include +#include +#include +#include +#include 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(); } diff --git a/PdfForQtViewer/pdfviewermainwindow.h b/PdfForQtViewer/pdfviewermainwindow.h index 1dc3810..2f0baf3 100644 --- a/PdfForQtViewer/pdfviewermainwindow.h +++ b/PdfForQtViewer/pdfviewermainwindow.h @@ -21,6 +21,7 @@ #include "pdfcatalog.h" #include "pdfrenderer.h" +#include #include #include @@ -33,6 +34,7 @@ namespace pdf { class PDFWidget; class PDFDocument; +class PDFOptionalContentTreeItemModel; } namespace pdfviewer @@ -82,6 +84,9 @@ private: QSharedPointer m_pdfDocument; QString m_directory; QString m_currentFile; + QDockWidget* m_optionalContentDockWidget; + QTreeView* m_optionalContentTreeView; + pdf::PDFOptionalContentTreeItemModel* m_optionalContentTreeModel; }; } // namespace pdfviewer