diff --git a/PdfForQtLib/sources/pdfcatalog.h b/PdfForQtLib/sources/pdfcatalog.h index ea5dbb8..19d2c25 100644 --- a/PdfForQtLib/sources/pdfcatalog.h +++ b/PdfForQtLib/sources/pdfcatalog.h @@ -234,6 +234,7 @@ public: PageLayout getPageLayout() const { return m_pageLayout; } PageMode getPageMode() const { return m_pageMode; } const QByteArray& getBaseURI() const { return m_baseURI; } + const std::map& getEmbeddedFiles() const { return m_embeddedFiles; } /// Returns destination using the key. If destination with the key is not found, /// then nullptr is returned. diff --git a/PdfForQtLib/sources/pdffile.h b/PdfForQtLib/sources/pdffile.h index c20e925..a44656a 100644 --- a/PdfForQtLib/sources/pdffile.h +++ b/PdfForQtLib/sources/pdffile.h @@ -32,6 +32,11 @@ public: explicit PDFEmbeddedFile() = default; bool isValid() const { return m_stream.isStream(); } + const QByteArray& getSubtype() const { return m_subtype; } + PDFInteger getSize() const { return m_size; } + const QDateTime& getCreationDate() const { return m_creationDate; } + const QDateTime& getModifiedDate() const { return m_modifiedDate; } + const QByteArray& getChecksum() const { return m_checksum; } static PDFEmbeddedFile parse(const PDFDocument* document, PDFObject object); diff --git a/PdfForQtLib/sources/pdfitemmodels.cpp b/PdfForQtLib/sources/pdfitemmodels.cpp index e6c5bda..fc65f7f 100644 --- a/PdfForQtLib/sources/pdfitemmodels.cpp +++ b/PdfForQtLib/sources/pdfitemmodels.cpp @@ -19,7 +19,10 @@ #include "pdfdocument.h" #include +#include #include +#include +#include namespace pdf { @@ -437,4 +440,152 @@ const PDFAction* PDFOutlineTreeItemModel::getAction(const QModelIndex& index) co return nullptr; } +int PDFAttachmentsTreeItemModel::columnCount(const QModelIndex& parent) const +{ + Q_UNUSED(parent); + return EndColumn; +} + +QVariant PDFAttachmentsTreeItemModel::data(const QModelIndex& index, int role) const +{ + if (!index.isValid()) + { + return QVariant(); + } + + const PDFAttachmentsTreeItem* item = static_cast(index.internalPointer()); + switch (role) + { + case Qt::DisplayRole: + { + switch (index.column()) + { + case Title: + return item->getTitle(); + + case Description: + return item->getDescription(); + + default: + Q_ASSERT(false); + break; + } + break; + } + + case Qt::DecorationRole: + { + switch (index.column()) + { + case Title: + return item->getIcon(); + + case Description: + return QVariant(); + + default: + Q_ASSERT(false); + break; + } + break; + } + + default: + break; + } + + return QVariant(); +} + +void PDFAttachmentsTreeItemModel::update() +{ + beginResetModel(); + + m_rootItem.reset(); + if (m_document) + { + const std::map& embeddedFiles = m_document->getCatalog()->getEmbeddedFiles(); + if (!embeddedFiles.empty()) + { + QMimeDatabase mimeDatabase; + QFileIconProvider fileIconProvider; + fileIconProvider.setOptions(QFileIconProvider::DontUseCustomDirectoryIcons); + PDFAttachmentsTreeItem* root = new PDFAttachmentsTreeItem(nullptr, QIcon(), QString(), QString(), nullptr); + m_rootItem.reset(root); + + std::map subroots; + + for (const auto& embeddedFile : embeddedFiles) + { + const PDFFileSpecification* specification = &embeddedFile.second; + const PDFEmbeddedFile* file = specification->getPlatformFile(); + if (!file) + { + continue; + } + + QString fileName = specification->getPlatformFileName(); + QString description = specification->getDescription(); + + // Jakub Melka: try to obtain mime type from subtype, if it fails, then form file name. + // We do not obtain type from data, because it can be slow (files can be large). + QMimeType type = mimeDatabase.mimeTypeForName(file->getSubtype()); + if (!type.isValid()) + { + type = mimeDatabase.mimeTypeForFile(fileName, QMimeDatabase::MatchExtension); + } + + // Get icon and select folder, to which file belongs + QIcon icon; + QString fileTypeName = "@GENERIC"; + QString fileTypeDescription = tr("Files"); + if (type.isValid()) + { + icon = QIcon::fromTheme(type.iconName()); + if (icon.isNull()) + { + icon = QIcon::fromTheme(type.genericIconName()); + } + + fileTypeName = type.name(); + fileTypeDescription = type.comment(); + } + + if (icon.isNull()) + { + icon = fileIconProvider.icon(QFileInfo(fileName)); + } + if (icon.isNull()) + { + icon = QApplication::style()->standardIcon(QStyle::SP_FileIcon); + } + + // Create subroot + PDFAttachmentsTreeItem* subroot = nullptr; + auto it = subroots.find(fileTypeName); + if (it == subroots.cend()) + { + subroot = new PDFAttachmentsTreeItem(nullptr, icon, fileTypeDescription, QString(), nullptr); + root->addCreatedChild(subroot); + subroots[fileTypeName] = subroot; + } + else + { + subroot = it->second; + } + + // Create item + subroot->addCreatedChild(new PDFAttachmentsTreeItem(nullptr, qMove(icon), qMove(fileName), qMove(description), specification)); + } + } + } + + endResetModel(); +} + +Qt::ItemFlags PDFAttachmentsTreeItemModel::flags(const QModelIndex& index) const +{ + return PDFTreeItemModel::flags(index); +} + } // namespace pdf diff --git a/PdfForQtLib/sources/pdfitemmodels.h b/PdfForQtLib/sources/pdfitemmodels.h index 891f11d..e22eec0 100644 --- a/PdfForQtLib/sources/pdfitemmodels.h +++ b/PdfForQtLib/sources/pdfitemmodels.h @@ -29,6 +29,7 @@ namespace pdf class PDFAction; class PDFDocument; class PDFOutlineItem; +class PDFFileSpecification; class PDFOptionalContentActivity; /// Represents tree item in the GUI tree @@ -166,6 +167,54 @@ private: QIcon m_icon; }; +class PDFAttachmentsTreeItem : public PDFTreeItem +{ +public: + explicit PDFAttachmentsTreeItem(PDFAttachmentsTreeItem* parent, QIcon icon, QString title, QString description, const PDFFileSpecification* fileSpecification) : + PDFTreeItem(parent), + m_icon(qMove(icon)), + m_title(qMove(title)), + m_description(qMove(description)), + m_fileSpecification(fileSpecification) + { + + } + + const QIcon& getIcon() const { return m_icon; } + const QString& getTitle() const { return m_title; } + const QString& getDescription() const { return m_description; } + const PDFFileSpecification* getFileSpecification() const { return m_fileSpecification; } + +private: + QIcon m_icon; + QString m_title; + QString m_description; + const PDFFileSpecification* m_fileSpecification; +}; + +class PDFFORQTLIBSHARED_EXPORT PDFAttachmentsTreeItemModel : public PDFTreeItemModel +{ + Q_OBJECT +public: + PDFAttachmentsTreeItemModel(QObject* parent) : + PDFTreeItemModel(parent) + { + + } + + enum Column + { + Title, + Description, + EndColumn + }; + + 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/PdfForQtViewer/pdfsidebarwidget.cpp b/PdfForQtViewer/pdfsidebarwidget.cpp index e632918..69cbacb 100644 --- a/PdfForQtViewer/pdfsidebarwidget.cpp +++ b/PdfForQtViewer/pdfsidebarwidget.cpp @@ -30,7 +30,8 @@ PDFSidebarWidget::PDFSidebarWidget(QWidget* parent) : m_outlineTreeModel(nullptr), m_optionalContentTreeModel(nullptr), m_document(nullptr), - m_optionalContentActivity(nullptr) + m_optionalContentActivity(nullptr), + m_attachmentsTreeModel(nullptr) { ui->setupUi(this); @@ -46,10 +47,18 @@ PDFSidebarWidget::PDFSidebarWidget(QWidget* parent) : m_optionalContentTreeModel = new pdf::PDFOptionalContentTreeItemModel(this); ui->optionalContentTreeView->setModel(m_optionalContentTreeModel); + // Attachments + ui->attachmentsTreeView->header()->hide(); + m_attachmentsTreeModel = new pdf::PDFAttachmentsTreeItemModel(this); + ui->attachmentsTreeView->setModel(m_attachmentsTreeModel); + ui->attachmentsTreeView->setSelectionMode(QAbstractItemView::SingleSelection); + ui->attachmentsTreeView->setSelectionBehavior(QAbstractItemView::SelectRows); + m_pageInfo[Invalid] = { nullptr, ui->emptyPage }; m_pageInfo[OptionalContent] = { ui->optionalContentButton, ui->optionalContentPage }; m_pageInfo[Bookmarks] = { ui->bookmarksButton, ui->bookmarksPage }; m_pageInfo[Thumbnails] = { ui->thumbnailsButton, ui->thumbnailsPage }; + m_pageInfo[Attachments] = { ui->attachmentsButton, ui->attachmentsPage }; setAutoFillBackground(true); selectPage(Invalid); @@ -74,6 +83,11 @@ void PDFSidebarWidget::setDocument(const pdf::PDFDocument* document, pdf::PDFOpt m_optionalContentTreeModel->setActivity(m_optionalContentActivity); ui->optionalContentTreeView->expandAll(); + // Update attachments + m_attachmentsTreeModel->setDocument(document); + ui->attachmentsTreeView->expandAll(); + ui->attachmentsTreeView->resizeColumnToContents(0); + Page preferred = Invalid; if (m_document) { @@ -92,6 +106,10 @@ void PDFSidebarWidget::setDocument(const pdf::PDFDocument* document, pdf::PDFOpt preferred = OptionalContent; break; + case pdf::PageMode::UseAttachments: + preferred = Attachments; + break; + default: break; } @@ -122,15 +140,18 @@ bool PDFSidebarWidget::isEmpty(Page page) const case Invalid: return true; - case OptionalContent: - return m_optionalContentTreeModel->isEmpty(); - case Bookmarks: return m_outlineTreeModel->isEmpty(); case Thumbnails: return true; + case OptionalContent: + return m_optionalContentTreeModel->isEmpty(); + + case Attachments: + return m_attachmentsTreeModel->isEmpty(); + default: Q_ASSERT(false); break; diff --git a/PdfForQtViewer/pdfsidebarwidget.h b/PdfForQtViewer/pdfsidebarwidget.h index bcb9dca..62e0a4e 100644 --- a/PdfForQtViewer/pdfsidebarwidget.h +++ b/PdfForQtViewer/pdfsidebarwidget.h @@ -34,6 +34,7 @@ namespace pdf class PDFAction; class PDFDocument; class PDFOutlineTreeItemModel; +class PDFAttachmentsTreeItemModel; class PDFOptionalContentActivity; class PDFOptionalContentTreeItemModel; } @@ -53,9 +54,10 @@ public: { Invalid, _BEGIN, - OptionalContent = _BEGIN, - Bookmarks, + Bookmarks = _BEGIN, Thumbnails, + OptionalContent, + Attachments, _END }; @@ -93,6 +95,7 @@ private: pdf::PDFOptionalContentTreeItemModel* m_optionalContentTreeModel; const pdf::PDFDocument* m_document; pdf::PDFOptionalContentActivity* m_optionalContentActivity; + pdf::PDFAttachmentsTreeItemModel* m_attachmentsTreeModel; std::map m_pageInfo; }; diff --git a/PdfForQtViewer/pdfsidebarwidget.ui b/PdfForQtViewer/pdfsidebarwidget.ui index f67baea..284be03 100644 --- a/PdfForQtViewer/pdfsidebarwidget.ui +++ b/PdfForQtViewer/pdfsidebarwidget.ui @@ -509,6 +509,19 @@ + + + + Attachments + + + true + + + true + + + @@ -527,7 +540,7 @@ - 3 + 4 @@ -1461,6 +1474,467 @@ + + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 127 + 127 + 127 + + + + + + + 170 + 170 + 170 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 127 + 127 + 127 + + + + + + + 170 + 170 + 170 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + + + 127 + 127 + 127 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 127 + 127 + 127 + + + + + + + 170 + 170 + 170 + + + + + + + 127 + 127 + 127 + + + + + + + 255 + 255 + 255 + + + + + + + 127 + 127 + 127 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + +