File indexing completed on 2024-04-28 15:51:57
0001 /* 0002 SPDX-FileCopyrightText: 2004 Albert Astals Cid <aacid@kde.org> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "propertiesdialog.h" 0008 0009 // qt/kde includes 0010 #include <QApplication> 0011 #include <QFile> 0012 #include <QFileDialog> 0013 #include <QFormLayout> 0014 #include <QHeaderView> 0015 #include <QIcon> 0016 #include <QLatin1Char> 0017 #include <QLayout> 0018 #include <QMenu> 0019 #include <QProgressBar> 0020 #include <QPushButton> 0021 #include <QSortFilterProxyModel> 0022 #include <QTableView> 0023 #include <QTimer> 0024 #include <QTreeView> 0025 0026 #include <KIconLoader> 0027 #include <KLocalizedString> 0028 #include <KMessageBox> 0029 #include <KSqueezedTextLabel> 0030 #include <QMimeDatabase> 0031 0032 // local includes 0033 #include "core/document.h" 0034 0035 static const int IsExtractableRole = Qt::UserRole; 0036 static const int FontInfoRole = Qt::UserRole + 1; 0037 0038 PropertiesDialog::PropertiesDialog(QWidget *parent, Okular::Document *doc) 0039 : KPageDialog(parent) 0040 , m_document(doc) 0041 , m_fontPage(nullptr) 0042 , m_fontModel(nullptr) 0043 , m_pageSizesModel(nullptr) 0044 , m_fontInfo(nullptr) 0045 , m_fontProgressBar(nullptr) 0046 , m_fontScanStarted(false) 0047 { 0048 setFaceType(Tabbed); 0049 setWindowTitle(i18n("Unknown File")); 0050 setStandardButtons(QDialogButtonBox::Ok); 0051 0052 // PROPERTIES 0053 QFrame *page = new QFrame(); 0054 KPageWidgetItem *item = addPage(page, i18n("&Properties")); 0055 item->setIcon(QIcon::fromTheme(QStringLiteral("document-properties"))); 0056 0057 // get document info 0058 const Okular::DocumentInfo info = doc->documentInfo(); 0059 QFormLayout *layout = new QFormLayout(page); 0060 0061 // mime name based on mimetype id 0062 QString mimeName = info.get(Okular::DocumentInfo::MimeType).section(QLatin1Char('/'), -1).toUpper(); 0063 setWindowTitle(i18n("%1 Properties", mimeName)); 0064 0065 /* obtains the properties list, conveniently ordered */ 0066 QStringList orderedProperties; 0067 orderedProperties << Okular::DocumentInfo::getKeyString(Okular::DocumentInfo::FilePath) << Okular::DocumentInfo::getKeyString(Okular::DocumentInfo::PagesSize) << Okular::DocumentInfo::getKeyString(Okular::DocumentInfo::DocumentSize); 0068 for (Okular::DocumentInfo::Key ks = Okular::DocumentInfo::Title; ks <= Okular::DocumentInfo::Keywords; ks = Okular::DocumentInfo::Key(ks + 1)) { 0069 orderedProperties << Okular::DocumentInfo::getKeyString(ks); 0070 } 0071 const QStringList infoKeys = info.keys(); 0072 for (const QString &ks : infoKeys) { 0073 if (!orderedProperties.contains(ks)) { 0074 orderedProperties << ks; 0075 } 0076 } 0077 0078 for (const QString &key : std::as_const(orderedProperties)) { 0079 const QString titleString = info.getKeyTitle(key); 0080 const QString valueString = info.get(key); 0081 if (titleString.isNull() || valueString.isNull()) { 0082 continue; 0083 } 0084 0085 // create labels and layout them 0086 QWidget *value = nullptr; 0087 if (key == Okular::DocumentInfo::getKeyString(Okular::DocumentInfo::MimeType)) { 0088 /// for mime type fields, show icon as well 0089 value = new QWidget(page); 0090 /// place icon left of mime type's name 0091 QHBoxLayout *hboxLayout = new QHBoxLayout(value); 0092 hboxLayout->setContentsMargins(0, 0, 0, 0); 0093 /// retrieve icon and place it in a QLabel 0094 QMimeDatabase db; 0095 QMimeType mimeType = db.mimeTypeForName(valueString); 0096 KSqueezedTextLabel *squeezed; 0097 if (mimeType.isValid()) { 0098 /// retrieve icon and place it in a QLabel 0099 QLabel *pixmapLabel = new QLabel(value); 0100 hboxLayout->addWidget(pixmapLabel, 0); 0101 const QIcon icon = QIcon::fromTheme(mimeType.iconName(), QIcon::fromTheme(QStringLiteral("application-octet-stream"))); 0102 pixmapLabel->setPixmap(icon.pixmap(KIconLoader::SizeSmall)); 0103 /// mime type's name and label 0104 squeezed = new KSqueezedTextLabel(i18nc("mimetype information, example: \"PDF Document (application/pdf)\"", "%1 (%2)", mimeType.comment(), valueString), value); 0105 } else { 0106 /// only mime type name 0107 squeezed = new KSqueezedTextLabel(valueString, value); 0108 } 0109 squeezed->setTextInteractionFlags(Qt::TextSelectableByMouse); 0110 hboxLayout->addWidget(squeezed, 1); 0111 } else { 0112 /// default for any other document information 0113 KSqueezedTextLabel *label = new KSqueezedTextLabel(valueString, page); 0114 label->setTextInteractionFlags(Qt::TextSelectableByMouse); 0115 value = label; 0116 } 0117 layout->addRow(new QLabel(i18n("%1:", titleString)), value); 0118 } 0119 0120 // FONTS 0121 if (doc->canProvideFontInformation()) { 0122 // create fonts tab and layout it 0123 QFrame *page2 = new QFrame(); 0124 m_fontPage = addPage(page2, i18n("&Fonts")); 0125 m_fontPage->setIcon(QIcon::fromTheme(QStringLiteral("dialog-text-and-font"))); 0126 QVBoxLayout *page2Layout = new QVBoxLayout(page2); 0127 // add a tree view 0128 QTreeView *view = new QTreeView(page2); 0129 view->setContextMenuPolicy(Qt::CustomContextMenu); 0130 connect(view, &QTreeView::customContextMenuRequested, this, &PropertiesDialog::showFontsMenu); 0131 page2Layout->addWidget(view); 0132 view->setRootIsDecorated(false); 0133 view->setAlternatingRowColors(true); 0134 view->setSortingEnabled(true); 0135 // creating a proxy model so we can sort the data 0136 QSortFilterProxyModel *proxymodel = new QSortFilterProxyModel(view); 0137 proxymodel->setDynamicSortFilter(true); 0138 proxymodel->setSortCaseSensitivity(Qt::CaseInsensitive); 0139 m_fontModel = new FontsListModel(view); 0140 proxymodel->setSourceModel(m_fontModel); 0141 view->setModel(proxymodel); 0142 view->sortByColumn(0, Qt::AscendingOrder); 0143 m_fontInfo = new QLabel(this); 0144 page2Layout->addWidget(m_fontInfo); 0145 m_fontInfo->setText(i18n("Reading font information...")); 0146 m_fontInfo->hide(); 0147 m_fontProgressBar = new QProgressBar(this); 0148 page2Layout->addWidget(m_fontProgressBar); 0149 m_fontProgressBar->setRange(0, 100); 0150 m_fontProgressBar->setValue(0); 0151 m_fontProgressBar->hide(); 0152 } 0153 0154 // PAGE SIZES 0155 if (!m_document->allPagesSize().isValid()) { 0156 // create page sizes tab tab and layout it when there are multiple page sizes 0157 QFrame *page3 = new QFrame(); 0158 KPageWidgetItem *pageSizesPage = addPage(page3, i18n("&Page Sizes")); 0159 pageSizesPage->setIcon(QIcon::fromTheme(QStringLiteral("view-pages-overview"))); 0160 QVBoxLayout *page3Layout = new QVBoxLayout(page3); 0161 0162 // Add a table view 0163 QTableView *view = new QTableView(page3); 0164 m_pageSizesModel = new PageSizesModel(view, m_document); 0165 page3Layout->addWidget(view); 0166 view->setModel(m_pageSizesModel); 0167 view->setAlternatingRowColors(true); 0168 view->setCornerButtonEnabled(false); 0169 view->resizeColumnsToContents(); 0170 view->verticalHeader()->hide(); 0171 0172 // Stretch the last column, which is the widest 0173 QHeaderView *headerView = view->horizontalHeader(); 0174 headerView->setSectionResizeMode(0, QHeaderView::Interactive); 0175 headerView->setSectionResizeMode(1, QHeaderView::Stretch); 0176 } 0177 0178 // KPageDialog is a bit buggy, it doesn't fix its own sizeHint, so we have to manually resize 0179 resize(layout->sizeHint()); 0180 0181 connect(pageWidget(), QOverload<KPageWidgetItem *, KPageWidgetItem *>::of(&KPageWidget::currentPageChanged), this, &PropertiesDialog::pageChanged); 0182 } 0183 0184 PropertiesDialog::~PropertiesDialog() 0185 { 0186 m_document->stopFontReading(); 0187 } 0188 0189 void PropertiesDialog::pageChanged(KPageWidgetItem *current, KPageWidgetItem *) 0190 { 0191 if (current == m_fontPage && !m_fontScanStarted) { 0192 connect(m_document, &Okular::Document::gotFont, m_fontModel, &FontsListModel::addFont); 0193 connect(m_document, &Okular::Document::fontReadingProgress, this, &PropertiesDialog::slotFontReadingProgress); 0194 connect(m_document, &Okular::Document::fontReadingEnded, this, &PropertiesDialog::slotFontReadingEnded); 0195 QTimer::singleShot(0, this, &PropertiesDialog::reallyStartFontReading); 0196 0197 m_fontScanStarted = true; 0198 } 0199 } 0200 0201 void PropertiesDialog::slotFontReadingProgress(int page) 0202 { 0203 m_fontProgressBar->setValue(m_fontProgressBar->maximum() * (page + 1) / m_document->pages()); 0204 } 0205 0206 void PropertiesDialog::slotFontReadingEnded() 0207 { 0208 m_fontInfo->hide(); 0209 m_fontProgressBar->hide(); 0210 } 0211 0212 void PropertiesDialog::reallyStartFontReading() 0213 { 0214 m_fontInfo->show(); 0215 m_fontProgressBar->show(); 0216 m_document->startFontReading(); 0217 } 0218 0219 void PropertiesDialog::showFontsMenu(const QPoint pos) 0220 { 0221 QTreeView *view = static_cast<QTreeView *>(sender()); 0222 QModelIndex index = view->indexAt(pos); 0223 if (index.data(IsExtractableRole).toBool()) { 0224 QMenu *menu = new QMenu(this); 0225 menu->addAction(i18nc("@action:inmenu", "&Extract Font")); 0226 const QAction *result = menu->exec(view->viewport()->mapToGlobal(pos)); 0227 if (result) { 0228 Okular::FontInfo fi = index.data(FontInfoRole).value<Okular::FontInfo>(); 0229 const QString caption = i18n("Where do you want to save %1?", fi.name()); 0230 const QString path = QFileDialog::getSaveFileName(this, caption, fi.name()); 0231 if (path.isEmpty()) { 0232 return; 0233 } 0234 0235 QFile f(path); 0236 if (f.open(QIODevice::WriteOnly)) { 0237 f.write(m_document->fontData(fi)); 0238 f.close(); 0239 } else { 0240 KMessageBox::error(this, i18n("Could not open \"%1\" for writing. File was not saved.", path)); 0241 } 0242 } 0243 } 0244 } 0245 0246 FontsListModel::FontsListModel(QObject *parent) 0247 : QAbstractTableModel(parent) 0248 { 0249 } 0250 0251 FontsListModel::~FontsListModel() 0252 { 0253 } 0254 0255 void FontsListModel::addFont(const Okular::FontInfo &fi) 0256 { 0257 beginInsertRows(QModelIndex(), m_fonts.size(), m_fonts.size()); 0258 0259 m_fonts << fi; 0260 0261 endInsertRows(); 0262 } 0263 0264 int FontsListModel::columnCount(const QModelIndex &parent) const 0265 { 0266 return parent.isValid() ? 0 : 3; 0267 } 0268 0269 static QString descriptionForFontType(Okular::FontInfo::FontType type) 0270 { 0271 switch (type) { 0272 case Okular::FontInfo::Type1: 0273 return i18n("Type 1"); 0274 break; 0275 case Okular::FontInfo::Type1C: 0276 return i18n("Type 1C"); 0277 break; 0278 case Okular::FontInfo::Type1COT: 0279 return i18nc("OT means OpenType", "Type 1C (OT)"); 0280 break; 0281 case Okular::FontInfo::Type3: 0282 return i18n("Type 3"); 0283 break; 0284 case Okular::FontInfo::TrueType: 0285 return i18n("TrueType"); 0286 break; 0287 case Okular::FontInfo::TrueTypeOT: 0288 return i18nc("OT means OpenType", "TrueType (OT)"); 0289 break; 0290 case Okular::FontInfo::CIDType0: 0291 return i18n("CID Type 0"); 0292 break; 0293 case Okular::FontInfo::CIDType0C: 0294 return i18n("CID Type 0C"); 0295 break; 0296 case Okular::FontInfo::CIDType0COT: 0297 return i18nc("OT means OpenType", "CID Type 0C (OT)"); 0298 break; 0299 case Okular::FontInfo::CIDTrueType: 0300 return i18n("CID TrueType"); 0301 break; 0302 case Okular::FontInfo::CIDTrueTypeOT: 0303 return i18nc("OT means OpenType", "CID TrueType (OT)"); 0304 break; 0305 case Okular::FontInfo::TeXPK: 0306 return i18n("TeX PK"); 0307 break; 0308 case Okular::FontInfo::TeXVirtual: 0309 return i18n("TeX virtual"); 0310 break; 0311 case Okular::FontInfo::TeXFontMetric: 0312 return i18n("TeX Font Metric"); 0313 break; 0314 case Okular::FontInfo::TeXFreeTypeHandled: 0315 return i18n("TeX FreeType-handled"); 0316 break; 0317 case Okular::FontInfo::Unknown: 0318 return i18nc("Unknown font type", "Unknown"); 0319 break; 0320 } 0321 return QString(); 0322 } 0323 0324 static QString pathOrDescription(const Okular::FontInfo &font) 0325 { 0326 switch (font.embedType()) { 0327 case Okular::FontInfo::NotEmbedded: 0328 return font.file(); 0329 break; 0330 case Okular::FontInfo::EmbeddedSubset: 0331 return i18n("Embedded (subset)"); 0332 break; 0333 case Okular::FontInfo::FullyEmbedded: 0334 return i18n("Fully embedded"); 0335 break; 0336 } 0337 return QString(); 0338 } 0339 0340 static QString descriptionForEmbedType(Okular::FontInfo::EmbedType type) 0341 { 0342 switch (type) { 0343 case Okular::FontInfo::NotEmbedded: 0344 return i18n("No"); 0345 break; 0346 case Okular::FontInfo::EmbeddedSubset: 0347 return i18n("Yes (subset)"); 0348 break; 0349 case Okular::FontInfo::FullyEmbedded: 0350 return i18n("Yes"); 0351 break; 0352 } 0353 return QString(); 0354 } 0355 0356 QVariant FontsListModel::data(const QModelIndex &index, int role) const 0357 { 0358 if (!index.isValid() || index.row() < 0 || index.row() >= m_fonts.count()) { 0359 return QVariant(); 0360 } 0361 0362 switch (role) { 0363 case Qt::DisplayRole: 0364 switch (index.column()) { 0365 case 0: { 0366 const Okular::FontInfo &fi = m_fonts.at(index.row()); 0367 const QString fontname = fi.name(); 0368 const QString substituteName = fi.substituteName(); 0369 if (fi.embedType() == Okular::FontInfo::NotEmbedded && !substituteName.isEmpty() && !fontname.isEmpty() && substituteName != fontname) { 0370 return i18nc("Replacing missing font with another one", "%1 (substituting with %2)", fontname, substituteName); 0371 } 0372 return fontname.isEmpty() ? i18nc("font name not available (empty)", "[n/a]") : fontname; 0373 } 0374 case 1: 0375 return descriptionForFontType(m_fonts.at(index.row()).type()); 0376 break; 0377 case 2: 0378 return pathOrDescription(m_fonts.at(index.row())); 0379 break; 0380 } 0381 break; 0382 case Qt::ToolTipRole: { 0383 QString fontname = m_fonts.at(index.row()).name(); 0384 if (fontname.isEmpty()) { 0385 fontname = i18n("Unknown font"); 0386 } 0387 QString tooltip = QLatin1String("<html><b>") + fontname + QLatin1String("</b>"); 0388 if (m_fonts.at(index.row()).embedType() == Okular::FontInfo::NotEmbedded) { 0389 tooltip += QStringLiteral(" (<span style=\"font-family: '%1'\">%2</span>)").arg(fontname, fontname); 0390 } 0391 tooltip += QLatin1String("<br />") + i18n("Embedded: %1", descriptionForEmbedType(m_fonts.at(index.row()).embedType())); 0392 tooltip += QLatin1String("</html>"); 0393 return tooltip; 0394 } 0395 case IsExtractableRole: { 0396 return m_fonts.at(index.row()).canBeExtracted(); 0397 } 0398 case FontInfoRole: { 0399 QVariant v; 0400 v.setValue(m_fonts.at(index.row())); 0401 return v; 0402 } 0403 } 0404 0405 return QVariant(); 0406 } 0407 0408 QVariant FontsListModel::headerData(int section, Qt::Orientation orientation, int role) const 0409 { 0410 if (orientation != Qt::Horizontal) { 0411 return QVariant(); 0412 } 0413 0414 if (role == Qt::TextAlignmentRole) { 0415 return QVariant(Qt::AlignLeft); 0416 } 0417 0418 if (role != Qt::DisplayRole) { 0419 return QVariant(); 0420 } 0421 0422 switch (section) { 0423 case 0: 0424 return i18n("Name"); 0425 break; 0426 case 1: 0427 return i18n("Type"); 0428 break; 0429 case 2: 0430 return i18n("File"); 0431 break; 0432 default: 0433 return QVariant(); 0434 } 0435 } 0436 0437 int FontsListModel::rowCount(const QModelIndex &parent) const 0438 { 0439 return parent.isValid() ? 0 : m_fonts.size(); 0440 } 0441 0442 PageSizesModel::PageSizesModel(QObject *parent, Okular::Document *doc) 0443 : QAbstractTableModel(parent) 0444 , m_document(doc) 0445 { 0446 } 0447 0448 PageSizesModel::~PageSizesModel() 0449 { 0450 } 0451 0452 int PageSizesModel::columnCount(const QModelIndex &parent) const 0453 { 0454 return parent.isValid() ? 0 : 2; 0455 } 0456 0457 QVariant PageSizesModel::data(const QModelIndex &index, int role) const 0458 { 0459 if (!index.isValid() || index.row() < 0 || index.row() >= (int)m_document->pages()) { 0460 return QVariant(); 0461 } 0462 0463 switch (index.column()) { 0464 case 0: { 0465 if (role == Qt::DisplayRole) { 0466 return index.row() + 1; // Page zero doesn't make sense to the user 0467 } else if (role == Qt::TextAlignmentRole) { 0468 return Qt::AlignCenter; 0469 } 0470 break; 0471 } 0472 case 1: 0473 if (role == Qt::DisplayRole) { 0474 return m_document->pageSizeString(index.row()); 0475 } 0476 break; 0477 } 0478 0479 return QVariant(); 0480 } 0481 0482 QVariant PageSizesModel::headerData(int section, Qt::Orientation orientation, int role) const 0483 { 0484 if (orientation != Qt::Horizontal) { 0485 return QVariant(); 0486 } 0487 0488 if (role == Qt::TextAlignmentRole) { 0489 return QVariant(Qt::AlignLeft); 0490 } 0491 0492 if (role != Qt::DisplayRole) { 0493 return QVariant(); 0494 } 0495 0496 switch (section) { 0497 case 0: 0498 return i18n("Page"); 0499 break; 0500 case 1: 0501 return i18n("Size"); 0502 break; 0503 default: 0504 return QVariant(); 0505 } 0506 } 0507 0508 int PageSizesModel::rowCount(const QModelIndex &parent) const 0509 { 0510 return parent.isValid() ? 0 : m_document->pages(); 0511 } 0512 0513 #include "moc_propertiesdialog.cpp" 0514 0515 /* kate: replace-tabs on; indent-width 4; */