File indexing completed on 2024-05-12 16:06:48
0001 /* 0002 SPDX-FileCopyrightText: 2007-2008 Albert Astals Cid <aacid@kde.org> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "generator_ghostview.h" 0008 0009 #include <math.h> 0010 0011 #include <QFile> 0012 #include <QPainter> 0013 #include <QPixmap> 0014 #include <QPrinter> 0015 #include <QSize> 0016 0017 #include <KAboutData> 0018 #include <KConfigDialog> 0019 #include <KLocalizedString> 0020 #include <QDebug> 0021 #include <QDir> 0022 #include <QMimeDatabase> 0023 #include <QMimeType> 0024 #include <QTemporaryFile> 0025 0026 #include <core/document.h> 0027 #include <core/fileprinter.h> 0028 #include <core/page.h> 0029 #include <core/utils.h> 0030 0031 #include "gssettings.h" 0032 #include "ui_gssettingswidget.h" 0033 0034 #include "rendererthread.h" 0035 #include "spectre_debug.h" 0036 0037 OKULAR_EXPORT_PLUGIN(GSGenerator, "libokularGenerator_ghostview.json") 0038 0039 GSGenerator::GSGenerator(QObject *parent, const QVariantList &args) 0040 : Okular::Generator(parent, args) 0041 , m_internalDocument(nullptr) 0042 , m_request(nullptr) 0043 { 0044 setFeature(PrintPostscript); 0045 setFeature(PrintToFile); 0046 0047 GSRendererThread *renderer = GSRendererThread::getCreateRenderer(); 0048 if (!renderer->isRunning()) { 0049 renderer->start(); 0050 } 0051 connect(renderer, &GSRendererThread::imageDone, this, &GSGenerator::slotImageGenerated, Qt::QueuedConnection); 0052 } 0053 0054 GSGenerator::~GSGenerator() 0055 { 0056 } 0057 0058 bool GSGenerator::reparseConfig() 0059 { 0060 bool changed = false; 0061 if (m_internalDocument) { 0062 #define SET_HINT(hintname, hintdefvalue, hintvar) \ 0063 { \ 0064 bool newhint = documentMetaData(hintname, hintdefvalue).toBool(); \ 0065 if (newhint != cache_##hintvar) { \ 0066 cache_##hintvar = newhint; \ 0067 changed = true; \ 0068 } \ 0069 } 0070 SET_HINT(GraphicsAntialiasMetaData, true, AAgfx) 0071 SET_HINT(TextAntialiasMetaData, true, AAtext) 0072 #undef SET_HINT 0073 } 0074 return changed; 0075 } 0076 0077 void GSGenerator::addPages(KConfigDialog *dlg) 0078 { 0079 Ui_GSSettingsWidget gsw; 0080 QWidget *w = new QWidget(dlg); 0081 gsw.setupUi(w); 0082 dlg->addPage(w, GSSettings::self(), i18n("Ghostscript"), QStringLiteral("okular-gv"), i18n("Ghostscript Backend Configuration")); 0083 } 0084 0085 Okular::Document::PrintError GSGenerator::print(QPrinter &printer) 0086 { 0087 // Create tempfile to write to 0088 QTemporaryFile tf(QDir::tempPath() + QLatin1String("/okular_XXXXXX.ps")); 0089 0090 // Get list of pages to print 0091 QList<int> pageList = Okular::FilePrinter::pageList(printer, spectre_document_get_n_pages(m_internalDocument), document()->currentPage() + 1, document()->bookmarkedPageList()); 0092 0093 // Default to Postscript export, but if printing to PDF use that instead 0094 SpectreExporterFormat exportFormat = SPECTRE_EXPORTER_FORMAT_PS; 0095 if (printer.outputFileName().right(3) == QLatin1String("pdf")) { 0096 exportFormat = SPECTRE_EXPORTER_FORMAT_PDF; 0097 tf.setFileTemplate(QDir::tempPath() + QLatin1String("/okular_XXXXXX.pdf")); 0098 } 0099 0100 if (!tf.open()) { 0101 return Okular::Document::TemporaryFileOpenPrintError; 0102 } 0103 0104 SpectreExporter *exporter = spectre_exporter_new(m_internalDocument, exportFormat); 0105 SpectreStatus exportStatus = spectre_exporter_begin(exporter, tf.fileName().toLatin1().constData()); 0106 0107 int i = 0; 0108 while (i < pageList.count() && exportStatus == SPECTRE_STATUS_SUCCESS) { 0109 exportStatus = spectre_exporter_do_page(exporter, pageList.at(i) - 1); 0110 i++; 0111 } 0112 0113 SpectreStatus endStatus = SPECTRE_STATUS_EXPORTER_ERROR; 0114 if (exportStatus == SPECTRE_STATUS_SUCCESS) { 0115 endStatus = spectre_exporter_end(exporter); 0116 } 0117 0118 spectre_exporter_free(exporter); 0119 0120 const QString printFileName = tf.fileName(); 0121 tf.close(); 0122 0123 if (exportStatus == SPECTRE_STATUS_SUCCESS && endStatus == SPECTRE_STATUS_SUCCESS) { 0124 tf.setAutoRemove(false); 0125 return Okular::FilePrinter::printFile(printer, printFileName, document()->orientation(), Okular::FilePrinter::SystemDeletesFiles, Okular::FilePrinter::ApplicationSelectsPages, document()->bookmarkedPageRange()); 0126 } 0127 0128 return Okular::Document::UnknownPrintError; 0129 } 0130 0131 bool GSGenerator::loadDocument(const QString &fileName, QVector<Okular::Page *> &pagesVector) 0132 { 0133 cache_AAtext = documentMetaData(TextAntialiasMetaData, true).toBool(); 0134 cache_AAgfx = documentMetaData(GraphicsAntialiasMetaData, true).toBool(); 0135 0136 m_internalDocument = spectre_document_new(); 0137 spectre_document_load(m_internalDocument, QFile::encodeName(fileName).constData()); 0138 const SpectreStatus loadStatus = spectre_document_status(m_internalDocument); 0139 if (loadStatus != SPECTRE_STATUS_SUCCESS) { 0140 qCDebug(OkularSpectreDebug) << "ERR:" << spectre_status_to_string(loadStatus); 0141 spectre_document_free(m_internalDocument); 0142 m_internalDocument = nullptr; 0143 return false; 0144 } 0145 pagesVector.resize(spectre_document_get_n_pages(m_internalDocument)); 0146 qCDebug(OkularSpectreDebug) << "Page count:" << pagesVector.count(); 0147 return loadPages(pagesVector); 0148 } 0149 0150 bool GSGenerator::doCloseDocument() 0151 { 0152 spectre_document_free(m_internalDocument); 0153 m_internalDocument = nullptr; 0154 0155 return true; 0156 } 0157 0158 void GSGenerator::slotImageGenerated(QImage *img, Okular::PixmapRequest *request) 0159 { 0160 // This can happen as GSInterpreterCMD is a singleton and on creation signals all the slots 0161 // of all the generators attached to it 0162 if (request != m_request) { 0163 return; 0164 } 0165 0166 if (!request->page()->isBoundingBoxKnown()) { 0167 updatePageBoundingBox(request->page()->number(), Okular::Utils::imageBoundingBox(img)); 0168 } 0169 0170 m_request = nullptr; 0171 QPixmap *pix = new QPixmap(QPixmap::fromImage(*img)); 0172 delete img; 0173 request->page()->setPixmap(request->observer(), pix); 0174 signalPixmapRequestDone(request); 0175 } 0176 0177 bool GSGenerator::loadPages(QVector<Okular::Page *> &pagesVector) 0178 { 0179 for (uint i = 0; i < spectre_document_get_n_pages(m_internalDocument); i++) { 0180 SpectrePage *page; 0181 int width = 0, height = 0; 0182 SpectreOrientation pageOrientation = SPECTRE_ORIENTATION_PORTRAIT; 0183 page = spectre_document_get_page(m_internalDocument, i); 0184 if (spectre_document_status(m_internalDocument)) { 0185 qCDebug(OkularSpectreDebug) << "Error getting page" << i << spectre_status_to_string(spectre_document_status(m_internalDocument)); 0186 } else { 0187 spectre_page_get_size(page, &width, &height); 0188 pageOrientation = spectre_page_get_orientation(page); 0189 } 0190 spectre_page_free(page); 0191 if (pageOrientation % 2 == 1) { 0192 std::swap(width, height); 0193 } 0194 pagesVector[i] = new Okular::Page(i, width, height, orientation(pageOrientation)); 0195 } 0196 return pagesVector.count() > 0; 0197 } 0198 0199 void GSGenerator::generatePixmap(Okular::PixmapRequest *req) 0200 { 0201 qCDebug(OkularSpectreDebug) << "receiving" << *req; 0202 0203 SpectrePage *page = spectre_document_get_page(m_internalDocument, req->pageNumber()); 0204 0205 GSRendererThread *renderer = GSRendererThread::getCreateRenderer(); 0206 0207 GSRendererThreadRequest gsreq(this); 0208 gsreq.spectrePage = page; 0209 gsreq.platformFonts = GSSettings::platformFonts(); 0210 int graphicsAA = 1; 0211 int textAA = 1; 0212 if (cache_AAgfx) { 0213 graphicsAA = 4; 0214 } 0215 if (cache_AAtext) { 0216 textAA = 4; 0217 } 0218 gsreq.textAAbits = textAA; 0219 gsreq.graphicsAAbits = graphicsAA; 0220 0221 gsreq.orientation = req->page()->orientation(); 0222 if (req->page()->rotation() == Okular::Rotation90 || req->page()->rotation() == Okular::Rotation270) { 0223 gsreq.magnify = qMax((double)req->height() / req->page()->width(), (double)req->width() / req->page()->height()); 0224 } else { 0225 gsreq.magnify = qMax((double)req->width() / req->page()->width(), (double)req->height() / req->page()->height()); 0226 } 0227 gsreq.request = req; 0228 m_request = req; 0229 renderer->addRequest(gsreq); 0230 } 0231 0232 bool GSGenerator::canGeneratePixmap() const 0233 { 0234 return !m_request; 0235 } 0236 0237 Okular::DocumentInfo GSGenerator::generateDocumentInfo(const QSet<Okular::DocumentInfo::Key> &keys) const 0238 { 0239 Okular::DocumentInfo docInfo; 0240 if (keys.contains(Okular::DocumentInfo::Title)) { 0241 docInfo.set(Okular::DocumentInfo::Title, QString::fromUtf8(spectre_document_get_title(m_internalDocument))); 0242 } 0243 if (keys.contains(Okular::DocumentInfo::Author)) { 0244 docInfo.set(Okular::DocumentInfo::Author, QString::fromUtf8(spectre_document_get_for(m_internalDocument))); 0245 } 0246 if (keys.contains(Okular::DocumentInfo::Creator)) { 0247 docInfo.set(Okular::DocumentInfo::Creator, QString::fromUtf8(spectre_document_get_creator(m_internalDocument))); 0248 } 0249 if (keys.contains(Okular::DocumentInfo::CreationDate)) { 0250 docInfo.set(Okular::DocumentInfo::CreationDate, QString::fromUtf8(spectre_document_get_creation_date(m_internalDocument))); 0251 } 0252 if (keys.contains(Okular::DocumentInfo::CustomKeys)) { 0253 docInfo.set(QStringLiteral("dscversion"), QString::fromUtf8(spectre_document_get_format(m_internalDocument)), i18n("Document version")); 0254 } 0255 0256 if (keys.contains(Okular::DocumentInfo::MimeType)) { 0257 int languageLevel = spectre_document_get_language_level(m_internalDocument); 0258 if (languageLevel > 0) { 0259 docInfo.set(QStringLiteral("langlevel"), QString::number(languageLevel), i18n("Language Level")); 0260 } 0261 if (spectre_document_is_eps(m_internalDocument)) { 0262 docInfo.set(Okular::DocumentInfo::MimeType, QStringLiteral("image/x-eps")); 0263 } else { 0264 docInfo.set(Okular::DocumentInfo::MimeType, QStringLiteral("application/postscript")); 0265 } 0266 } 0267 0268 if (keys.contains(Okular::DocumentInfo::Pages)) { 0269 docInfo.set(Okular::DocumentInfo::Pages, QString::number(spectre_document_get_n_pages(m_internalDocument))); 0270 } 0271 0272 return docInfo; 0273 } 0274 0275 Okular::Rotation GSGenerator::orientation(SpectreOrientation pageOrientation) const 0276 { 0277 switch (pageOrientation) { 0278 case SPECTRE_ORIENTATION_PORTRAIT: 0279 return Okular::Rotation0; 0280 case SPECTRE_ORIENTATION_LANDSCAPE: 0281 return Okular::Rotation90; 0282 case SPECTRE_ORIENTATION_REVERSE_PORTRAIT: 0283 return Okular::Rotation180; 0284 case SPECTRE_ORIENTATION_REVERSE_LANDSCAPE: 0285 return Okular::Rotation270; 0286 } 0287 // get rid of warnings, should never happen 0288 return Okular::Rotation0; 0289 } 0290 0291 QVariant GSGenerator::metaData(const QString &key, const QVariant &option) const 0292 { 0293 Q_UNUSED(option) 0294 if (key == QLatin1String("DocumentTitle")) { 0295 const char *title = spectre_document_get_title(m_internalDocument); 0296 if (title) { 0297 return QString::fromLatin1(title); 0298 } 0299 } 0300 return QVariant(); 0301 } 0302 0303 #include "generator_ghostview.moc"