/* SPDX-FileCopyrightText: 2004 Albert Astals Cid SPDX-License-Identifier: GPL-2.0-or-later */ #include "propertiesdialog.h" // qt/kde includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // local includes #include "core/document.h" static const int IsExtractableRole = Qt::UserRole; static const int FontInfoRole = Qt::UserRole + 1; PropertiesDialog::PropertiesDialog(QWidget *parent, Okular::Document *doc) : KPageDialog(parent) , m_document(doc) , m_fontPage(nullptr) , m_fontModel(nullptr) , m_pageSizesModel(nullptr) , m_fontInfo(nullptr) , m_fontProgressBar(nullptr) , m_fontScanStarted(false) { setFaceType(Tabbed); setWindowTitle(i18n("Unknown File")); setStandardButtons(QDialogButtonBox::Ok); // PROPERTIES QFrame *page = new QFrame(); KPageWidgetItem *item = addPage(page, i18n("&Properties")); item->setIcon(QIcon::fromTheme(QStringLiteral("document-properties"))); // get document info const Okular::DocumentInfo info = doc->documentInfo(); QFormLayout *layout = new QFormLayout(page); // mime name based on mimetype id QString mimeName = info.get(Okular::DocumentInfo::MimeType).section(QLatin1Char('/'), -1).toUpper(); setWindowTitle(i18n("%1 Properties", mimeName)); /* obtains the properties list, conveniently ordered */ QStringList orderedProperties; orderedProperties << Okular::DocumentInfo::getKeyString(Okular::DocumentInfo::FilePath) << Okular::DocumentInfo::getKeyString(Okular::DocumentInfo::PagesSize) << Okular::DocumentInfo::getKeyString(Okular::DocumentInfo::DocumentSize); for (Okular::DocumentInfo::Key ks = Okular::DocumentInfo::Title; ks <= Okular::DocumentInfo::Keywords; ks = Okular::DocumentInfo::Key(ks + 1)) { orderedProperties << Okular::DocumentInfo::getKeyString(ks); } const QStringList infoKeys = info.keys(); for (const QString &ks : infoKeys) { if (!orderedProperties.contains(ks)) { orderedProperties << ks; } } for (const QString &key : std::as_const(orderedProperties)) { const QString titleString = info.getKeyTitle(key); const QString valueString = info.get(key); if (titleString.isNull() || valueString.isNull()) { continue; } // create labels and layout them QWidget *value = nullptr; if (key == Okular::DocumentInfo::getKeyString(Okular::DocumentInfo::MimeType)) { /// for mime type fields, show icon as well value = new QWidget(page); /// place icon left of mime type's name QHBoxLayout *hboxLayout = new QHBoxLayout(value); hboxLayout->setContentsMargins(0, 0, 0, 0); /// retrieve icon and place it in a QLabel QMimeDatabase db; QMimeType mimeType = db.mimeTypeForName(valueString); KSqueezedTextLabel *squeezed; if (mimeType.isValid()) { /// retrieve icon and place it in a QLabel QLabel *pixmapLabel = new QLabel(value); hboxLayout->addWidget(pixmapLabel, 0); const QIcon icon = QIcon::fromTheme(mimeType.iconName(), QIcon::fromTheme(QStringLiteral("application-octet-stream"))); pixmapLabel->setPixmap(icon.pixmap(KIconLoader::SizeSmall)); /// mime type's name and label squeezed = new KSqueezedTextLabel(i18nc("mimetype information, example: \"PDF Document (application/pdf)\"", "%1 (%2)", mimeType.comment(), valueString), value); } else { /// only mime type name squeezed = new KSqueezedTextLabel(valueString, value); } squeezed->setTextInteractionFlags(Qt::TextSelectableByMouse); hboxLayout->addWidget(squeezed, 1); } else { /// default for any other document information KSqueezedTextLabel *label = new KSqueezedTextLabel(valueString, page); label->setTextInteractionFlags(Qt::TextSelectableByMouse); value = label; } layout->addRow(new QLabel(i18n("%1:", titleString)), value); } // FONTS if (doc->canProvideFontInformation()) { // create fonts tab and layout it QFrame *page2 = new QFrame(); m_fontPage = addPage(page2, i18n("&Fonts")); m_fontPage->setIcon(QIcon::fromTheme(QStringLiteral("dialog-text-and-font"))); QVBoxLayout *page2Layout = new QVBoxLayout(page2); // add a tree view QTreeView *view = new QTreeView(page2); view->setContextMenuPolicy(Qt::CustomContextMenu); connect(view, &QTreeView::customContextMenuRequested, this, &PropertiesDialog::showFontsMenu); page2Layout->addWidget(view); view->setRootIsDecorated(false); view->setAlternatingRowColors(true); view->setSortingEnabled(true); // creating a proxy model so we can sort the data QSortFilterProxyModel *proxymodel = new QSortFilterProxyModel(view); proxymodel->setDynamicSortFilter(true); proxymodel->setSortCaseSensitivity(Qt::CaseInsensitive); m_fontModel = new FontsListModel(view); proxymodel->setSourceModel(m_fontModel); view->setModel(proxymodel); view->sortByColumn(0, Qt::AscendingOrder); m_fontInfo = new QLabel(this); page2Layout->addWidget(m_fontInfo); m_fontInfo->setText(i18n("Reading font information...")); m_fontInfo->hide(); m_fontProgressBar = new QProgressBar(this); page2Layout->addWidget(m_fontProgressBar); m_fontProgressBar->setRange(0, 100); m_fontProgressBar->setValue(0); m_fontProgressBar->hide(); } // PAGE SIZES if (!m_document->allPagesSize().isValid()) { // create page sizes tab tab and layout it when there are multiple page sizes QFrame *page3 = new QFrame(); KPageWidgetItem *pageSizesPage = addPage(page3, i18n("&Page Sizes")); pageSizesPage->setIcon(QIcon::fromTheme(QStringLiteral("view-pages-overview"))); QVBoxLayout *page3Layout = new QVBoxLayout(page3); // Add a table view QTableView *view = new QTableView(page3); m_pageSizesModel = new PageSizesModel(view, m_document); page3Layout->addWidget(view); view->setModel(m_pageSizesModel); view->setAlternatingRowColors(true); view->setCornerButtonEnabled(false); view->resizeColumnsToContents(); view->verticalHeader()->hide(); // Stretch the last column, which is the widest QHeaderView *headerView = view->horizontalHeader(); headerView->setSectionResizeMode(0, QHeaderView::Interactive); headerView->setSectionResizeMode(1, QHeaderView::Stretch); } // KPageDialog is a bit buggy, it doesn't fix its own sizeHint, so we have to manually resize resize(layout->sizeHint()); connect(pageWidget(), QOverload::of(&KPageWidget::currentPageChanged), this, &PropertiesDialog::pageChanged); } PropertiesDialog::~PropertiesDialog() { m_document->stopFontReading(); } void PropertiesDialog::pageChanged(KPageWidgetItem *current, KPageWidgetItem *) { if (current == m_fontPage && !m_fontScanStarted) { connect(m_document, &Okular::Document::gotFont, m_fontModel, &FontsListModel::addFont); connect(m_document, &Okular::Document::fontReadingProgress, this, &PropertiesDialog::slotFontReadingProgress); connect(m_document, &Okular::Document::fontReadingEnded, this, &PropertiesDialog::slotFontReadingEnded); QTimer::singleShot(0, this, &PropertiesDialog::reallyStartFontReading); m_fontScanStarted = true; } } void PropertiesDialog::slotFontReadingProgress(int page) { m_fontProgressBar->setValue(m_fontProgressBar->maximum() * (page + 1) / m_document->pages()); } void PropertiesDialog::slotFontReadingEnded() { m_fontInfo->hide(); m_fontProgressBar->hide(); } void PropertiesDialog::reallyStartFontReading() { m_fontInfo->show(); m_fontProgressBar->show(); m_document->startFontReading(); } void PropertiesDialog::showFontsMenu(const QPoint pos) { QTreeView *view = static_cast(sender()); QModelIndex index = view->indexAt(pos); if (index.data(IsExtractableRole).toBool()) { QMenu *menu = new QMenu(this); menu->addAction(i18nc("@action:inmenu", "&Extract Font")); const QAction *result = menu->exec(view->viewport()->mapToGlobal(pos)); if (result) { Okular::FontInfo fi = index.data(FontInfoRole).value(); const QString caption = i18n("Where do you want to save %1?", fi.name()); const QString path = QFileDialog::getSaveFileName(this, caption, fi.name()); if (path.isEmpty()) { return; } QFile f(path); if (f.open(QIODevice::WriteOnly)) { f.write(m_document->fontData(fi)); f.close(); } else { KMessageBox::error(this, i18n("Could not open \"%1\" for writing. File was not saved.", path)); } } } } FontsListModel::FontsListModel(QObject *parent) : QAbstractTableModel(parent) { } FontsListModel::~FontsListModel() { } void FontsListModel::addFont(const Okular::FontInfo &fi) { beginInsertRows(QModelIndex(), m_fonts.size(), m_fonts.size()); m_fonts << fi; endInsertRows(); } int FontsListModel::columnCount(const QModelIndex &parent) const { return parent.isValid() ? 0 : 3; } static QString descriptionForFontType(Okular::FontInfo::FontType type) { switch (type) { case Okular::FontInfo::Type1: return i18n("Type 1"); break; case Okular::FontInfo::Type1C: return i18n("Type 1C"); break; case Okular::FontInfo::Type1COT: return i18nc("OT means OpenType", "Type 1C (OT)"); break; case Okular::FontInfo::Type3: return i18n("Type 3"); break; case Okular::FontInfo::TrueType: return i18n("TrueType"); break; case Okular::FontInfo::TrueTypeOT: return i18nc("OT means OpenType", "TrueType (OT)"); break; case Okular::FontInfo::CIDType0: return i18n("CID Type 0"); break; case Okular::FontInfo::CIDType0C: return i18n("CID Type 0C"); break; case Okular::FontInfo::CIDType0COT: return i18nc("OT means OpenType", "CID Type 0C (OT)"); break; case Okular::FontInfo::CIDTrueType: return i18n("CID TrueType"); break; case Okular::FontInfo::CIDTrueTypeOT: return i18nc("OT means OpenType", "CID TrueType (OT)"); break; case Okular::FontInfo::TeXPK: return i18n("TeX PK"); break; case Okular::FontInfo::TeXVirtual: return i18n("TeX virtual"); break; case Okular::FontInfo::TeXFontMetric: return i18n("TeX Font Metric"); break; case Okular::FontInfo::TeXFreeTypeHandled: return i18n("TeX FreeType-handled"); break; case Okular::FontInfo::Unknown: return i18nc("Unknown font type", "Unknown"); break; } return QString(); } static QString pathOrDescription(const Okular::FontInfo &font) { switch (font.embedType()) { case Okular::FontInfo::NotEmbedded: return font.file(); break; case Okular::FontInfo::EmbeddedSubset: return i18n("Embedded (subset)"); break; case Okular::FontInfo::FullyEmbedded: return i18n("Fully embedded"); break; } return QString(); } static QString descriptionForEmbedType(Okular::FontInfo::EmbedType type) { switch (type) { case Okular::FontInfo::NotEmbedded: return i18n("No"); break; case Okular::FontInfo::EmbeddedSubset: return i18n("Yes (subset)"); break; case Okular::FontInfo::FullyEmbedded: return i18n("Yes"); break; } return QString(); } QVariant FontsListModel::data(const QModelIndex &index, int role) const { if (!index.isValid() || index.row() < 0 || index.row() >= m_fonts.count()) { return QVariant(); } switch (role) { case Qt::DisplayRole: switch (index.column()) { case 0: { const Okular::FontInfo &fi = m_fonts.at(index.row()); const QString fontname = fi.name(); const QString substituteName = fi.substituteName(); if (fi.embedType() == Okular::FontInfo::NotEmbedded && !substituteName.isEmpty() && !fontname.isEmpty() && substituteName != fontname) { return i18nc("Replacing missing font with another one", "%1 (substituting with %2)", fontname, substituteName); } return fontname.isEmpty() ? i18nc("font name not available (empty)", "[n/a]") : fontname; } case 1: return descriptionForFontType(m_fonts.at(index.row()).type()); break; case 2: return pathOrDescription(m_fonts.at(index.row())); break; } break; case Qt::ToolTipRole: { QString fontname = m_fonts.at(index.row()).name(); if (fontname.isEmpty()) { fontname = i18n("Unknown font"); } QString tooltip = QLatin1String("") + fontname + QLatin1String(""); if (m_fonts.at(index.row()).embedType() == Okular::FontInfo::NotEmbedded) { tooltip += QStringLiteral(" (%2)").arg(fontname, fontname); } tooltip += QLatin1String("
") + i18n("Embedded: %1", descriptionForEmbedType(m_fonts.at(index.row()).embedType())); tooltip += QLatin1String(""); return tooltip; } case IsExtractableRole: { return m_fonts.at(index.row()).canBeExtracted(); } case FontInfoRole: { QVariant v; v.setValue(m_fonts.at(index.row())); return v; } } return QVariant(); } QVariant FontsListModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation != Qt::Horizontal) { return QVariant(); } if (role == Qt::TextAlignmentRole) { return QVariant(Qt::AlignLeft); } if (role != Qt::DisplayRole) { return QVariant(); } switch (section) { case 0: return i18n("Name"); break; case 1: return i18n("Type"); break; case 2: return i18n("File"); break; default: return QVariant(); } } int FontsListModel::rowCount(const QModelIndex &parent) const { return parent.isValid() ? 0 : m_fonts.size(); } PageSizesModel::PageSizesModel(QObject *parent, Okular::Document *doc) : QAbstractTableModel(parent) , m_document(doc) { } PageSizesModel::~PageSizesModel() { } int PageSizesModel::columnCount(const QModelIndex &parent) const { return parent.isValid() ? 0 : 2; } QVariant PageSizesModel::data(const QModelIndex &index, int role) const { if (!index.isValid() || index.row() < 0 || index.row() >= (int)m_document->pages()) { return QVariant(); } switch (index.column()) { case 0: { if (role == Qt::DisplayRole) { return index.row() + 1; // Page zero doesn't make sense to the user } else if (role == Qt::TextAlignmentRole) { return Qt::AlignCenter; } break; } case 1: if (role == Qt::DisplayRole) { return m_document->pageSizeString(index.row()); } break; } return QVariant(); } QVariant PageSizesModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation != Qt::Horizontal) { return QVariant(); } if (role == Qt::TextAlignmentRole) { return QVariant(Qt::AlignLeft); } if (role != Qt::DisplayRole) { return QVariant(); } switch (section) { case 0: return i18n("Page"); break; case 1: return i18n("Size"); break; default: return QVariant(); } } int PageSizesModel::rowCount(const QModelIndex &parent) const { return parent.isValid() ? 0 : m_document->pages(); } #include "moc_propertiesdialog.cpp" /* kate: replace-tabs on; indent-width 4; */